/* * 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.app.AlarmManager; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; 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.ServiceConnection; 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.Pair; 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.widget.IRemoteViewsAdapterConnection; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; 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"; /* * When identifying a Host or Provider based on the calling process, use the uid field. * When identifying a Host or Provider based on a package manager broadcast, use the * package given. */ static class Provider { int uid; AppWidgetProviderInfo info; ArrayList instances = new ArrayList(); PendingIntent broadcast; boolean zombie; // if we're in safe mode, don't prune this just because nobody references it int tag; // for use while saving state (the index) } static class Host { int uid; int hostId; String packageName; ArrayList instances = new ArrayList(); IAppWidgetHost callbacks; boolean zombie; // if we're in safe mode, don't prune this just because nobody references it int tag; // for use while saving state (the index) } static class AppWidgetId { int appWidgetId; Provider provider; RemoteViews views; Host host; } /** * Acts as a proxy between the ServiceConnection and the RemoteViewsAdapterConnection. * This needs to be a static inner class since a reference to the ServiceConnection is held * globally and may lead us to leak AppWidgetService instances (if there were more than one). */ static class ServiceConnectionProxy implements ServiceConnection { private final IBinder mConnectionCb; ServiceConnectionProxy(Pair key, IBinder connectionCb) { mConnectionCb = connectionCb; } public void onServiceConnected(ComponentName name, IBinder service) { final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb); try { cb.onServiceConnected(service); } catch (Exception e) { e.printStackTrace(); } } public void onServiceDisconnected(ComponentName name) { disconnect(); } public void disconnect() { final IRemoteViewsAdapterConnection cb = IRemoteViewsAdapterConnection.Stub.asInterface(mConnectionCb); try { cb.onServiceDisconnected(); } catch (Exception e) { e.printStackTrace(); } } } Context mContext; Locale mLocale; PackageManager mPackageManager; AlarmManager mAlarmManager; ArrayList mInstalledProviders = new ArrayList(); int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1; final ArrayList mAppWidgetIds = new ArrayList(); ArrayList mHosts = new ArrayList(); boolean mSafeMode; private final SparseArray mAppWidgetServices; AppWidgetService(Context context) { mContext = context; mAppWidgetServices = new SparseArray(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); mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)); } }, userFilter); IntentFilter userStopFilter = new IntentFilter(); userStopFilter.addAction(Intent.ACTION_USER_STOPPED); mContext.registerReceiverAsUser(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { onUserStopped(getSendingUserId()); } }, UserHandle.ALL, userFilter, null, null); } private int getCallingOrCurrentUserId() { int callingUid = Binder.getCallingUid(); if (callingUid == android.os.Process.myUid()) { 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 updatedViews) throws RemoteException { return getImplForUser(getCallingOrCurrentUserId()).startListening(host, packageName, hostId, updatedViews); } public void onUserRemoved(int userId) { AppWidgetServiceImpl impl = mAppWidgetServices.get(userId); if (userId < 1) return; if (impl == null) { AppWidgetServiceImpl.getSettingsFile(userId).delete(); } else { impl.onUserRemoved(); } } public void onUserStopped(int userId) { } private AppWidgetServiceImpl getImplForUser(int userId) { AppWidgetServiceImpl service = mAppWidgetServices.get(userId); if (service == null) { Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user"); // 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. service.sendInitialBroadcasts(); mAppWidgetServices.append(userId, service); } 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); } static int[] getAppWidgetIds(Provider p) { int instancesSize = p.instances.size(); int appWidgetIds[] = new int[instancesSize]; for (int i=0; i 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) { // Dump the state of all the app widget providers for (int i = 0; i < mAppWidgetServices.size(); i++) { AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i); service.dump(fd, pw, args); } } 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, -1); if (userId >= 0) { getImplForUser(userId).sendInitialBroadcasts(); } else { Slog.w(TAG, "Not 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); } } } } }; }