/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server; import android.app.ActivityManagerNative; import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; import android.util.SparseArray; import android.widget.RemoteViews; import com.android.internal.appwidget.IAppWidgetHost; import com.android.internal.appwidget.IAppWidgetService; import com.android.internal.util.IndentingPrintWriter; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; import java.util.Locale; /** * Redirects calls to this service to the instance of the service for the appropriate user. */ class AppWidgetService extends IAppWidgetService.Stub { private static final String TAG = "AppWidgetService"; Context mContext; Locale mLocale; PackageManager mPackageManager; boolean mSafeMode; private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices; AppWidgetService(Context context) { mContext = context; mAppWidgetServices = new SparseArray<AppWidgetServiceImpl>(5); AppWidgetServiceImpl primary = new AppWidgetServiceImpl(context, 0); mAppWidgetServices.append(0, primary); } public void systemReady(boolean safeMode) { mSafeMode = safeMode; mAppWidgetServices.get(0).systemReady(safeMode); // Register for the boot completed broadcast, so we can send the // ENABLE broacasts. If we try to send them now, they time out, // because the system isn't ready to handle them yet. mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null); // Register for configuration changes so we can update the names // of the widgets when the locale changes. mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED), null, null); // Register for broadcasts about package install, etc., so we can // update the provider list. IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_ADDED); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, sdFilter, null, null); IntentFilter userFilter = new IntentFilter(); userFilter.addAction(Intent.ACTION_USER_REMOVED); userFilter.addAction(Intent.ACTION_USER_STOPPING); mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)); } else if (Intent.ACTION_USER_STOPPING.equals(intent.getAction())) { onUserStopping(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL)); } } }, userFilter); } /** * This returns the user id of the caller, if the caller is not the system process, * otherwise it assumes that the calls are from the lockscreen and hence are meant for the * current user. TODO: Instead, have lockscreen make explicit calls with userId */ private int getCallingOrCurrentUserId() { int callingUid = Binder.getCallingUid(); // Also check the PID because Settings (power control widget) also runs as System UID if (callingUid == android.os.Process.myUid() && Binder.getCallingPid() == android.os.Process.myPid()) { try { return ActivityManagerNative.getDefault().getCurrentUser().id; } catch (RemoteException re) { return UserHandle.getUserId(callingUid); } } else { return UserHandle.getUserId(callingUid); } } @Override public int allocateAppWidgetId(String packageName, int hostId) throws RemoteException { return getImplForUser(getCallingOrCurrentUserId()).allocateAppWidgetId( packageName, hostId); } @Override public void deleteAppWidgetId(int appWidgetId) throws RemoteException { getImplForUser(getCallingOrCurrentUserId()).deleteAppWidgetId(appWidgetId); } @Override public void deleteHost(int hostId) throws RemoteException { getImplForUser(getCallingOrCurrentUserId()).deleteHost(hostId); } @Override public void deleteAllHosts() throws RemoteException { getImplForUser(getCallingOrCurrentUserId()).deleteAllHosts(); } @Override public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) throws RemoteException { getImplForUser(getCallingOrCurrentUserId()).bindAppWidgetId(appWidgetId, provider, options); } @Override public boolean bindAppWidgetIdIfAllowed( String packageName, int appWidgetId, ComponentName provider, Bundle options) throws RemoteException { return getImplForUser(getCallingOrCurrentUserId()).bindAppWidgetIdIfAllowed( packageName, appWidgetId, provider, options); } @Override public boolean hasBindAppWidgetPermission(String packageName) throws RemoteException { return getImplForUser(getCallingOrCurrentUserId()).hasBindAppWidgetPermission( packageName); } @Override public void setBindAppWidgetPermission(String packageName, boolean permission) throws RemoteException { getImplForUser(getCallingOrCurrentUserId()).setBindAppWidgetPermission( packageName, permission); } @Override public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) throws RemoteException { getImplForUser(getCallingOrCurrentUserId()).bindRemoteViewsService( appWidgetId, intent, connection); } @Override public int[] startListening(IAppWidgetHost host, String packageName, int hostId, List<RemoteViews> updatedViews) throws RemoteException { return getImplForUser(getCallingOrCurrentUserId()).startListening(host, packageName, hostId, updatedViews); } public void onUserRemoved(int userId) { if (userId < 1) return; synchronized (mAppWidgetServices) { AppWidgetServiceImpl impl = mAppWidgetServices.get(userId); mAppWidgetServices.remove(userId); if (impl == null) { AppWidgetServiceImpl.getSettingsFile(userId).delete(); } else { impl.onUserRemoved(); } } } public void onUserStopping(int userId) { if (userId < 1) return; synchronized (mAppWidgetServices) { AppWidgetServiceImpl impl = mAppWidgetServices.get(userId); if (impl != null) { mAppWidgetServices.remove(userId); impl.onUserStopping(); } } } private AppWidgetServiceImpl getImplForUser(int userId) { boolean sendInitial = false; AppWidgetServiceImpl service; synchronized (mAppWidgetServices) { service = mAppWidgetServices.get(userId); if (service == null) { Slog.i(TAG, "Unable to find AppWidgetServiceImpl for user " + userId + ", adding"); // TODO: Verify that it's a valid user service = new AppWidgetServiceImpl(mContext, userId); service.systemReady(mSafeMode); // Assume that BOOT_COMPLETED was received, as this is a non-primary user. mAppWidgetServices.append(userId, service); sendInitial = true; } } if (sendInitial) { service.sendInitialBroadcasts(); } return service; } @Override public int[] getAppWidgetIds(ComponentName provider) throws RemoteException { return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetIds(provider); } @Override public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) throws RemoteException { return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetInfo(appWidgetId); } @Override public RemoteViews getAppWidgetViews(int appWidgetId) throws RemoteException { return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetViews(appWidgetId); } @Override public void updateAppWidgetOptions(int appWidgetId, Bundle options) { getImplForUser(getCallingOrCurrentUserId()).updateAppWidgetOptions(appWidgetId, options); } @Override public Bundle getAppWidgetOptions(int appWidgetId) { return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetOptions(appWidgetId); } @Override public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException { return getImplForUser(getCallingOrCurrentUserId()).getInstalledProviders(); } @Override public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) throws RemoteException { getImplForUser(getCallingOrCurrentUserId()).notifyAppWidgetViewDataChanged( appWidgetIds, viewId); } @Override public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException { getImplForUser(getCallingOrCurrentUserId()).partiallyUpdateAppWidgetIds( appWidgetIds, views); } @Override public void stopListening(int hostId) throws RemoteException { getImplForUser(getCallingOrCurrentUserId()).stopListening(hostId); } @Override public void unbindRemoteViewsService(int appWidgetId, Intent intent) throws RemoteException { getImplForUser(getCallingOrCurrentUserId()).unbindRemoteViewsService( appWidgetId, intent); } @Override public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException { getImplForUser(getCallingOrCurrentUserId()).updateAppWidgetIds(appWidgetIds, views); } @Override public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) throws RemoteException { getImplForUser(getCallingOrCurrentUserId()).updateAppWidgetProvider(provider, views); } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); // Dump the state of all the app widget providers synchronized (mAppWidgetServices) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); for (int i = 0; i < mAppWidgetServices.size(); i++) { pw.println("User: " + mAppWidgetServices.keyAt(i)); ipw.increaseIndent(); AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i); service.dump(fd, ipw, args); ipw.decreaseIndent(); } } } BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // Slog.d(TAG, "received " + action); if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); if (userId >= 0) { getImplForUser(userId).sendInitialBroadcasts(); } else { Slog.w(TAG, "Incorrect user handle supplied in " + intent); } } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { for (int i = 0; i < mAppWidgetServices.size(); i++) { AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i); service.onConfigurationChanged(); } } else { int sendingUser = getSendingUserId(); if (sendingUser == UserHandle.USER_ALL) { for (int i = 0; i < mAppWidgetServices.size(); i++) { AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i); service.onBroadcastReceived(intent); } } else { AppWidgetServiceImpl service = mAppWidgetServices.get(sendingUser); if (service != null) { service.onBroadcastReceived(intent); } } } } }; }