Merge change 23323 into eclair

* changes:
      For intent ACTION_PACKAGE_CHANGED, there could be a lot of broadcasts related to enabling/disabling     components by apps which could result in thrashing on the PackageManager. For apps that     do not want to be restarted when such a broadcast is sent, we can just aggregate these broadcasts and     handle them at one go.     Changes include:     New structure to hold pending broadcasts by class name. If a component is enabled or disabled frequently     aggregate component enabled/disabled settings in this structure in a 10 second window and then     send out the accumulated list of broadcasts to the ActivityManager.     A new Handler implementation handles this message     Add new attribute name EXTRA_CHANGED_COMPONENT_NAME in broadcast intent Intent.ACTION_PACKAGE_CHANGED for     additional information for apps like Launcher.     Rename a couple of parameters, the names were too jarring.
This commit is contained in:
Android (Google) Code Review
2009-09-02 12:37:56 -07:00
2 changed files with 103 additions and 19 deletions

View File

@@ -1267,6 +1267,8 @@ public class Intent implements Parcelable {
* enabled or disabled. The data contains the name of the package. * enabled or disabled. The data contains the name of the package.
* <ul> * <ul>
* <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package.
* <li> {@link #EXTRA_CHANGED_COMPONENT_NAME} containing the class name of the changed component.
* <li> {@link #EXTRA_DONT_KILL_APP} containing boolean field to override the default action of restarting the application.
* </ul> * </ul>
* *
* <p class="note">This is a protected intent that can only be sent * <p class="note">This is a protected intent that can only be sent
@@ -2034,6 +2036,14 @@ public class Intent implements Parcelable {
public static final String EXTRA_REMOTE_INTENT_TOKEN = public static final String EXTRA_REMOTE_INTENT_TOKEN =
"android.intent.extra.remote_intent_token"; "android.intent.extra.remote_intent_token";
/**
* Used as an int extra field in {@link android.content.Intent#ACTION_PACKAGE_CHANGED}
* intent to supply the name of the component that changed.
* @hide
*/
public static final String EXTRA_CHANGED_COMPONENT_NAME =
"android.intent.extra.changed_component_name";
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Intent flags (see mFlags variable). // Intent flags (see mFlags variable).

View File

@@ -62,6 +62,8 @@ import android.os.Binder;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel; import android.os.Parcel;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.Environment; import android.os.Environment;
@@ -140,7 +142,7 @@ class PackageManagerService extends IPackageManager.Stub {
final HandlerThread mHandlerThread = new HandlerThread("PackageManager", final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
Process.THREAD_PRIORITY_BACKGROUND); Process.THREAD_PRIORITY_BACKGROUND);
final Handler mHandler; final PackageHandler mHandler;
final int mSdkVersion = Build.VERSION.SDK_INT; final int mSdkVersion = Build.VERSION.SDK_INT;
final String mSdkCodename = "REL".equals(Build.VERSION.CODENAME) final String mSdkCodename = "REL".equals(Build.VERSION.CODENAME)
@@ -272,6 +274,49 @@ class PackageManagerService extends IPackageManager.Stub {
ComponentName mResolveComponentName; ComponentName mResolveComponentName;
PackageParser.Package mPlatformPackage; PackageParser.Package mPlatformPackage;
// Set of pending broadcasts for aggregating enable/disable of components.
final HashMap<String, String> mPendingBroadcasts = new HashMap<String, String>();
static final int SEND_PENDING_BROADCAST = 1;
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
class PackageHandler extends Handler {
PackageHandler(Looper looper) {
super(looper);
}
public void handleMessage(Message msg) {
switch (msg.what) {
case SEND_PENDING_BROADCAST : {
int size = 0;
String broadcastList[];
HashMap<String, String> tmpMap;
int uids[];
synchronized (mPackages) {
size = mPendingBroadcasts.size();
if (size <= 0) {
// Nothing to be done. Just return
return;
}
broadcastList = new String[size];
mPendingBroadcasts.keySet().toArray(broadcastList);
tmpMap = new HashMap<String, String>(mPendingBroadcasts);
uids = new int[size];
for (int i = 0; i < size; i++) {
PackageSetting ps = mSettings.mPackages.get(mPendingBroadcasts.get(broadcastList[i]));
uids[i] = (ps != null) ? ps.userId : -1;
}
mPendingBroadcasts.clear();
}
// Send broadcasts
for (int i = 0; i < size; i++) {
String className = broadcastList[i];
sendPackageChangedBroadcast(className, true, tmpMap.get(className), uids[i]);
}
break;
}
}
}
}
public static final IPackageManager main(Context context, boolean factoryTest) { public static final IPackageManager main(Context context, boolean factoryTest) {
PackageManagerService m = new PackageManagerService(context, factoryTest); PackageManagerService m = new PackageManagerService(context, factoryTest);
ServiceManager.addService("package", m); ServiceManager.addService("package", m);
@@ -355,7 +400,7 @@ class PackageManagerService extends IPackageManager.Stub {
synchronized (mInstallLock) { synchronized (mInstallLock) {
synchronized (mPackages) { synchronized (mPackages) {
mHandlerThread.start(); mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper()); mHandler = new PackageHandler(mHandlerThread.getLooper());
File dataDir = Environment.getDataDirectory(); File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data"); mAppDataDir = new File(dataDir, "data");
@@ -4866,7 +4911,7 @@ class PackageManagerService extends IPackageManager.Stub {
} }
private void setEnabledSetting( private void setEnabledSetting(
final String packageNameStr, String classNameStr, int newState, final int flags) { final String packageName, String className, int newState, final int flags) {
if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT
|| newState == COMPONENT_ENABLED_STATE_ENABLED || newState == COMPONENT_ENABLED_STATE_ENABLED
|| newState == COMPONENT_ENABLED_STATE_DISABLED)) { || newState == COMPONENT_ENABLED_STATE_DISABLED)) {
@@ -4878,17 +4923,20 @@ class PackageManagerService extends IPackageManager.Stub {
final int permission = mContext.checkCallingPermission( final int permission = mContext.checkCallingPermission(
android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED); final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
boolean sendNow = false;
boolean isApp = (className == null);
String key = isApp ? packageName : className;
int packageUid = -1; int packageUid = -1;
synchronized (mPackages) { synchronized (mPackages) {
pkgSetting = mSettings.mPackages.get(packageNameStr); pkgSetting = mSettings.mPackages.get(packageName);
if (pkgSetting == null) { if (pkgSetting == null) {
if (classNameStr == null) { if (className == null) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Unknown package: " + packageNameStr); "Unknown package: " + packageName);
} }
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Unknown component: " + packageNameStr "Unknown component: " + packageName
+ "/" + classNameStr); + "/" + className);
} }
if (!allowedByPermission && (uid != pkgSetting.userId)) { if (!allowedByPermission && (uid != pkgSetting.userId)) {
throw new SecurityException( throw new SecurityException(
@@ -4896,41 +4944,67 @@ class PackageManagerService extends IPackageManager.Stub {
+ Binder.getCallingPid() + Binder.getCallingPid()
+ ", uid=" + uid + ", package uid=" + pkgSetting.userId); + ", uid=" + uid + ", package uid=" + pkgSetting.userId);
} }
packageUid = pkgSetting.userId; if (className == null) {
if (classNameStr == null) {
// We're dealing with an application/package level state change // We're dealing with an application/package level state change
pkgSetting.enabled = newState; pkgSetting.enabled = newState;
} else { } else {
// We're dealing with a component level state change // We're dealing with a component level state change
switch (newState) { switch (newState) {
case COMPONENT_ENABLED_STATE_ENABLED: case COMPONENT_ENABLED_STATE_ENABLED:
pkgSetting.enableComponentLP(classNameStr); pkgSetting.enableComponentLP(className);
break; break;
case COMPONENT_ENABLED_STATE_DISABLED: case COMPONENT_ENABLED_STATE_DISABLED:
pkgSetting.disableComponentLP(classNameStr); pkgSetting.disableComponentLP(className);
break; break;
case COMPONENT_ENABLED_STATE_DEFAULT: case COMPONENT_ENABLED_STATE_DEFAULT:
pkgSetting.restoreComponentLP(classNameStr); pkgSetting.restoreComponentLP(className);
break; break;
default: default:
Log.e(TAG, "Invalid new component state: " + newState); Log.e(TAG, "Invalid new component state: " + newState);
return;
} }
} }
mSettings.writeLP(); mSettings.writeLP();
packageUid = pkgSetting.userId;
if ((flags&PackageManager.DONT_KILL_APP) == 0) {
sendNow = true;
// Purge entry from pending broadcast list if another one exists already
// since we are sending one right away.
if (mPendingBroadcasts.get(key) != null) {
mPendingBroadcasts.remove(key);
// Can ignore empty list since its handled in the handler anyway
}
} else {
if (mPendingBroadcasts.get(key) == null) {
mPendingBroadcasts.put(key, packageName);
}
if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
// Schedule a message
mHandler.sendEmptyMessageDelayed(SEND_PENDING_BROADCAST, BROADCAST_DELAY);
}
}
} }
long callingId = Binder.clearCallingIdentity(); long callingId = Binder.clearCallingIdentity();
try { try {
Bundle extras = new Bundle(2); if (sendNow) {
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, sendPackageChangedBroadcast(packageName,
(flags&PackageManager.DONT_KILL_APP) != 0); (flags&PackageManager.DONT_KILL_APP) != 0, key, packageUid);
extras.putInt(Intent.EXTRA_UID, packageUid); }
sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageNameStr, extras);
} finally { } finally {
Binder.restoreCallingIdentity(callingId); Binder.restoreCallingIdentity(callingId);
} }
} }
private void sendPackageChangedBroadcast(String packageName,
boolean killFlag, String componentName, int packageUid) {
Bundle extras = new Bundle(2);
extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentName);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
extras.putInt(Intent.EXTRA_UID, packageUid);
sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras);
}
public String getInstallerPackageName(String packageName) { public String getInstallerPackageName(String packageName) {
synchronized (mPackages) { synchronized (mPackages) {
PackageSetting pkg = mSettings.mPackages.get(packageName); PackageSetting pkg = mSettings.mPackages.get(packageName);