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:
@@ -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).
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user