| /* |
| * 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); |
| } |
| } |
| } |
| } |
| }; |
| } |