Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 86a72dac authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Fix issue #2256270: Package manager sends bad broadcasts when components change

Also reworks this intent protocol a little bit to be much more efficient, only
requiring one broadcast per package.

Change-Id: I580de4843ebd3c7f2e6df7295a2f80d2937cef7c
parent 532d5603
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -36136,6 +36136,17 @@
 visibility="public"
>
</field>
<field name="EXTRA_CHANGED_COMPONENT_NAME_LIST"
 type="java.lang.String"
 transient="false"
 volatile="false"
 value="&quot;android.intent.extra.changed_component_name_list&quot;"
 static="true"
 final="true"
 deprecated="not deprecated"
 visibility="public"
>
</field>
<field name="EXTRA_DATA_REMOVED"
 type="java.lang.String"
 transient="false"
+16 −7
Original line number Diff line number Diff line
@@ -1275,12 +1275,15 @@ public class Intent implements Parcelable {
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
    /**
     * Broadcast Action: An existing application package has been changed (e.g. a component has been
     * enabled or disabled.  The data contains the name of the package.
     * Broadcast Action: An existing application package has been changed (e.g.
     * a component has been enabled or disabled).  The data contains the name of
     * the package.
     * <ul>
     * <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.
     * <li> {@link #EXTRA_CHANGED_COMPONENT_NAME_LIST} containing the class name
     * of the changed components.
     * <li> {@link #EXTRA_DONT_KILL_APP} containing boolean field to override the
     * default action of restarting the application.
     * </ul>
     * 
     * <p class="note">This is a protected intent that can only be sent
@@ -2101,13 +2104,19 @@ public class Intent implements Parcelable {
            "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.
     *
     * @Deprecated See {@link #EXTRA_CHANGED_COMPONENT_NAME_LIST}; this field
     * will contain only the first name in the list.
     */
    public static final String EXTRA_CHANGED_COMPONENT_NAME =
            "android.intent.extra.changed_component_name";

    /**
     * This field is part of {@link android.content.Intent#ACTION_PACKAGE_CHANGED}
     * and contains a string array of all of the components that have changed.
     */
    public static final String EXTRA_CHANGED_COMPONENT_NAME_LIST =
            "android.intent.extra.changed_component_name_list";

    /**
     * @hide
     * Magic extra system code can use when binding, to give a label for
+43 −21
Original line number Diff line number Diff line
@@ -277,7 +277,8 @@ class PackageManagerService extends IPackageManager.Stub {
    PackageParser.Package mPlatformPackage;

    // Set of pending broadcasts for aggregating enable/disable of components.
    final HashMap<String, String> mPendingBroadcasts = new HashMap<String, String>();
    final HashMap<String, ArrayList<String>> mPendingBroadcasts
            = new HashMap<String, ArrayList<String>>();
    static final int SEND_PENDING_BROADCAST = 1;
    // Delay time in millisecs
    static final int BROADCAST_DELAY = 10 * 1000;
@@ -289,30 +290,40 @@ class PackageManagerService extends IPackageManager.Stub {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SEND_PENDING_BROADCAST : {
                    String packages[];
                    ArrayList components[];
                    int size = 0;
                    String broadcastList[];
                    HashMap<String, String> tmpMap;
                    int uids[];
                    synchronized (mPackages) {
                        if (mPendingBroadcasts == null) {
                            return;
                        }
                        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);
                        packages = new String[size];
                        components = new ArrayList[size];
                        uids = new int[size];
                        for (int i = 0; i < size; i++) {
                            PackageSetting ps = mSettings.mPackages.get(mPendingBroadcasts.get(broadcastList[i]));
                        Iterator<HashMap.Entry<String, ArrayList<String>>>
                                it = mPendingBroadcasts.entrySet().iterator();
                        int i = 0;
                        while (it.hasNext() && i < size) {
                            HashMap.Entry<String, ArrayList<String>> ent = it.next();
                            packages[i] = ent.getKey();
                            components[i] = ent.getValue();
                            PackageSetting ps = mSettings.mPackages.get(ent.getKey());
                            uids[i] = (ps != null) ? ps.userId : -1;
                            i++;
                        }
                        size = i;
                        mPendingBroadcasts.clear();
                    }
                    // Send broadcasts
                    for (int i = 0; i < size; i++) {
                        String className = broadcastList[i];
                        sendPackageChangedBroadcast(className, true, tmpMap.get(className), uids[i]);
                        sendPackageChangedBroadcast(packages[i], true,
                                (ArrayList<String>)components[i], uids[i]);
                    }
                    break;
                }
@@ -5023,8 +5034,9 @@ class PackageManagerService extends IPackageManager.Stub {
        final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
        boolean sendNow = false;
        boolean isApp = (className == null);
        String key = isApp ? packageName : className;
        String componentName = isApp ? packageName : className;
        int packageUid = -1;
        ArrayList<String> components;
        synchronized (mPackages) {
            pkgSetting = mSettings.mPackages.get(packageName);
            if (pkgSetting == null) {
@@ -5064,17 +5076,22 @@ class PackageManagerService extends IPackageManager.Stub {
            }
            mSettings.writeLP();
            packageUid = pkgSetting.userId;
            components = mPendingBroadcasts.get(packageName);
            boolean newPackage = components == null;
            if (newPackage) {
                components = new ArrayList<String>();
            }
            if (!components.contains(componentName)) {
                components.add(componentName);
            }
            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
                }
                mPendingBroadcasts.remove(packageName);
            } else {
                if (mPendingBroadcasts.get(key) == null) {
                    mPendingBroadcasts.put(key, packageName);
                if (newPackage) {
                    mPendingBroadcasts.put(packageName, components);
                }
                if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
                    // Schedule a message
@@ -5087,7 +5104,7 @@ class PackageManagerService extends IPackageManager.Stub {
        try {
            if (sendNow) {
                sendPackageChangedBroadcast(packageName,
                        (flags&PackageManager.DONT_KILL_APP) != 0, key, packageUid);
                        (flags&PackageManager.DONT_KILL_APP) != 0, components, packageUid);
            }
        } finally {
            Binder.restoreCallingIdentity(callingId);
@@ -5095,9 +5112,14 @@ class PackageManagerService extends IPackageManager.Stub {
    }

    private void sendPackageChangedBroadcast(String packageName,
            boolean killFlag, String componentName, int packageUid) {
        Bundle extras = new Bundle(2);
        extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentName);
            boolean killFlag, ArrayList<String> componentNames, int packageUid) {
        if (false) Log.v(TAG, "Sending package changed: package=" + packageName
                + " components=" + componentNames);
        Bundle extras = new Bundle(4);
        extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
        String nameList[] = new String[componentNames.size()];
        componentNames.toArray(nameList);
        extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
        extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
        extras.putInt(Intent.EXTRA_UID, packageUid);
        sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED,  packageName, extras);