am 360d710e: Merge change I580de484 into eclair

Merge commit '360d710ebccc2a2b53bf6233e722d3287d585476' into eclair-plus-aosp

* commit '360d710ebccc2a2b53bf6233e722d3287d585476':
  Fix issue #2256270: Package manager sends bad broadcasts when components change
diff --git a/api/current.xml b/api/current.xml
index 13e4c94..d6af516 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -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"
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c676a5b..398f211 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1275,12 +1275,15 @@
     @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,14 +2104,20 @@
             "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
      * who it is that has bound to a service.  This is an integer giving
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index a83459e..39129d4 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -277,7 +277,8 @@
     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 @@
         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 @@
         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 @@
             }
             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 @@
         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 @@
     }
 
     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);