Merge "Fix widget cross-talk between users due to Settings widget" into jb-mr1-dev

This commit is contained in:
Amith Yamasani
2012-10-05 19:13:46 -07:00
committed by Android (Google) Code Review
2 changed files with 63 additions and 117 deletions

View File

@@ -17,34 +17,28 @@
package com.android.server; package com.android.server;
import android.app.ActivityManagerNative; import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo; import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Binder; import android.os.Binder;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.UserHandle; import android.os.UserHandle;
import android.util.Pair;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import com.android.internal.appwidget.IAppWidgetHost; import com.android.internal.appwidget.IAppWidgetHost;
import com.android.internal.appwidget.IAppWidgetService; import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.widget.IRemoteViewsAdapterConnection; import com.android.internal.util.IndentingPrintWriter;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@@ -56,85 +50,11 @@ class AppWidgetService extends IAppWidgetService.Stub
{ {
private static final String TAG = "AppWidgetService"; 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<AppWidgetId> instances = new ArrayList<AppWidgetId>();
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<AppWidgetId> instances = new ArrayList<AppWidgetId>();
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<Integer, Intent.FilterComparison> 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; Context mContext;
Locale mLocale; Locale mLocale;
PackageManager mPackageManager; PackageManager mPackageManager;
AlarmManager mAlarmManager;
ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
ArrayList<Host> mHosts = new ArrayList<Host>();
boolean mSafeMode; boolean mSafeMode;
private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices; private final SparseArray<AppWidgetServiceImpl> mAppWidgetServices;
AppWidgetService(Context context) { AppWidgetService(Context context) {
@@ -195,9 +115,16 @@ class AppWidgetService extends IAppWidgetService.Stub
}, UserHandle.ALL, userFilter, null, null); }, UserHandle.ALL, userFilter, null, null);
} }
/**
* 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() { private int getCallingOrCurrentUserId() {
int callingUid = Binder.getCallingUid(); int callingUid = Binder.getCallingUid();
if (callingUid == android.os.Process.myUid()) { // 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 { try {
return ActivityManagerNative.getDefault().getCurrentUser().id; return ActivityManagerNative.getDefault().getCurrentUser().id;
} catch (RemoteException re) { } catch (RemoteException re) {
@@ -272,13 +199,16 @@ class AppWidgetService extends IAppWidgetService.Stub
} }
public void onUserRemoved(int userId) { public void onUserRemoved(int userId) {
AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
if (userId < 1) return; if (userId < 1) return;
synchronized (mAppWidgetServices) {
if (impl == null) { AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
AppWidgetServiceImpl.getSettingsFile(userId).delete(); mAppWidgetServices.remove(userId);
} else {
impl.onUserRemoved(); if (impl == null) {
AppWidgetServiceImpl.getSettingsFile(userId).delete();
} else {
impl.onUserRemoved();
}
} }
} }
@@ -286,17 +216,23 @@ class AppWidgetService extends IAppWidgetService.Stub
} }
private AppWidgetServiceImpl getImplForUser(int userId) { private AppWidgetServiceImpl getImplForUser(int userId) {
AppWidgetServiceImpl service = mAppWidgetServices.get(userId); boolean sendInitial = false;
if (service == null) { AppWidgetServiceImpl service;
Slog.e(TAG, "Unable to find AppWidgetServiceImpl for the current user"); synchronized (mAppWidgetServices) {
// TODO: Verify that it's a valid user service = mAppWidgetServices.get(userId);
service = new AppWidgetServiceImpl(mContext, userId); if (service == null) {
service.systemReady(mSafeMode); Slog.i(TAG, "Unable to find AppWidgetServiceImpl for user " + userId + ", adding");
// Assume that BOOT_COMPLETED was received, as this is a non-primary user. // TODO: Verify that it's a valid user
service.sendInitialBroadcasts(); service = new AppWidgetServiceImpl(mContext, userId);
mAppWidgetServices.append(userId, service); 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; return service;
} }
@@ -325,15 +261,6 @@ class AppWidgetService extends IAppWidgetService.Stub
return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetOptions(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<instancesSize; i++) {
appWidgetIds[i] = p.instances.get(i).appWidgetId;
}
return appWidgetIds;
}
@Override @Override
public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException { public List<AppWidgetProviderInfo> getInstalledProviders() throws RemoteException {
return getImplForUser(getCallingOrCurrentUserId()).getInstalledProviders(); return getImplForUser(getCallingOrCurrentUserId()).getInstalledProviders();
@@ -378,9 +305,15 @@ class AppWidgetService extends IAppWidgetService.Stub
@Override @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
// Dump the state of all the app widget providers // Dump the state of all the app widget providers
for (int i = 0; i < mAppWidgetServices.size(); i++) { synchronized (mAppWidgetServices) {
AppWidgetServiceImpl service = mAppWidgetServices.valueAt(i); IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
service.dump(fd, pw, args); 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();
}
} }
} }

View File

@@ -87,6 +87,8 @@ class AppWidgetServiceImpl {
private static final String SETTINGS_FILENAME = "appwidgets.xml"; private static final String SETTINGS_FILENAME = "appwidgets.xml";
private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
private static boolean DBG = false;
/* /*
* When identifying a Host or Provider based on the calling process, use the uid field. When * 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. * identifying a Host or Provider based on a package manager broadcast, use the package given.
@@ -208,7 +210,12 @@ class AppWidgetServiceImpl {
} }
} }
private void log(String msg) {
Slog.i(TAG, "u=" + mUserId + ": " + msg);
}
void onConfigurationChanged() { void onConfigurationChanged() {
if (DBG) log("Got onConfigurationChanged()");
Locale revised = Locale.getDefault(); Locale revised = Locale.getDefault();
if (revised == null || mLocale == null || !(revised.equals(mLocale))) { if (revised == null || mLocale == null || !(revised.equals(mLocale))) {
mLocale = revised; mLocale = revised;
@@ -235,6 +242,7 @@ class AppWidgetServiceImpl {
} }
void onBroadcastReceived(Intent intent) { void onBroadcastReceived(Intent intent) {
if (DBG) log("onBroadcast " + intent);
final String action = intent.getAction(); final String action = intent.getAction();
boolean added = false; boolean added = false;
boolean changed = false; boolean changed = false;
@@ -425,7 +433,8 @@ class AppWidgetServiceImpl {
mAppWidgetIds.add(id); mAppWidgetIds.add(id);
saveStateLocked(); saveStateLocked();
if (DBG) log("Allocating AppWidgetId for " + packageName + " host=" + hostId
+ " id=" + appWidgetId);
return appWidgetId; return appWidgetId;
} }
} }
@@ -518,6 +527,7 @@ class AppWidgetServiceImpl {
} }
void cancelBroadcasts(Provider p) { void cancelBroadcasts(Provider p) {
if (DBG) log("cancelBroadcasts for " + p);
if (p.broadcast != null) { if (p.broadcast != null) {
mAlarmManager.cancel(p.broadcast); mAlarmManager.cancel(p.broadcast);
long token = Binder.clearCallingIdentity(); long token = Binder.clearCallingIdentity();
@@ -531,6 +541,8 @@ class AppWidgetServiceImpl {
} }
private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) { private void bindAppWidgetIdImpl(int appWidgetId, ComponentName provider, Bundle options) {
if (DBG) log("bindAppWidgetIdImpl appwid=" + appWidgetId
+ " provider=" + provider);
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
synchronized (mAppWidgetIds) { synchronized (mAppWidgetIds) {
@@ -825,12 +837,14 @@ class AppWidgetServiceImpl {
} }
public RemoteViews getAppWidgetViews(int appWidgetId) { public RemoteViews getAppWidgetViews(int appWidgetId) {
if (DBG) log("getAppWidgetViews id=" + appWidgetId);
synchronized (mAppWidgetIds) { synchronized (mAppWidgetIds) {
ensureStateLoadedLocked(); ensureStateLoadedLocked();
AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId); AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
if (id != null) { if (id != null) {
return cloneIfLocalBinder(id.views); return cloneIfLocalBinder(id.views);
} }
if (DBG) log(" couldn't find appwidgetid");
return null; return null;
} }
} }
@@ -854,7 +868,7 @@ class AppWidgetServiceImpl {
if (appWidgetIds == null) { if (appWidgetIds == null) {
return; return;
} }
if (DBG) log("updateAppWidgetIds views: " + views);
int bitmapMemoryUsage = 0; int bitmapMemoryUsage = 0;
if (views != null) { if (views != null) {
bitmapMemoryUsage = views.estimateMemoryUsage(); bitmapMemoryUsage = views.estimateMemoryUsage();
@@ -1280,8 +1294,8 @@ class AppWidgetServiceImpl {
intent.setComponent(p.info.provider); intent.setComponent(p.info.provider);
long token = Binder.clearCallingIdentity(); long token = Binder.clearCallingIdentity();
try { try {
p.broadcast = PendingIntent.getBroadcast(mContext, 1, intent, p.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent,
PendingIntent.FLAG_UPDATE_CURRENT); PendingIntent.FLAG_UPDATE_CURRENT, new UserHandle(mUserId));
} finally { } finally {
Binder.restoreCallingIdentity(token); Binder.restoreCallingIdentity(token);
} }
@@ -1353,7 +1367,7 @@ class AppWidgetServiceImpl {
p.uid = activityInfo.applicationInfo.uid; p.uid = activityInfo.applicationInfo.uid;
Resources res = mContext.getPackageManager() Resources res = mContext.getPackageManager()
.getResourcesForApplication(activityInfo.applicationInfo); .getResourcesForApplicationAsUser(activityInfo.packageName, mUserId);
TypedArray sa = res.obtainAttributes(attrs, TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AppWidgetProviderInfo); com.android.internal.R.styleable.AppWidgetProviderInfo);
@@ -1597,8 +1611,7 @@ class AppWidgetServiceImpl {
final IPackageManager packageManager = AppGlobals.getPackageManager(); final IPackageManager packageManager = AppGlobals.getPackageManager();
try { try {
packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, packageManager.getReceiverInfo(new ComponentName(pkg, cl), 0, mUserId);
UserHandle.getCallingUserId());
} catch (RemoteException e) { } catch (RemoteException e) {
String[] pkgs = mContext.getPackageManager() String[] pkgs = mContext.getPackageManager()
.currentToCanonicalPackageNames(new String[] { pkg }); .currentToCanonicalPackageNames(new String[] { pkg });