Merge change 1110 into donut

* changes:
  Track install/removal/update of packages that provide backup services
diff --git a/core/java/android/backup/BackupService.java b/core/java/android/backup/BackupService.java
index 5a6886d..6ac703a 100644
--- a/core/java/android/backup/BackupService.java
+++ b/core/java/android/backup/BackupService.java
@@ -33,7 +33,7 @@
  * In order to use the backup service, your application must implement a
  * subclass of BackupService, and declare an intent filter
  * in the application manifest specifying that your BackupService subclass
- * handles the {link #SERVICE_ACTION} intent action.  For example:
+ * handles the {@link BackupService#SERVICE_ACTION} intent action.  For example:
  * 
  * <pre class="prettyprint">
  *      &lt;!-- Use the class "MyBackupService" to perform backups for my app --&gt;
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 56caeea..63fc871 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -18,14 +18,18 @@
 
 import android.backup.BackupService;
 import android.backup.IBackupService;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.net.Uri;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
@@ -207,30 +211,127 @@
         mStateDir.mkdirs();
         mDataDir = Environment.getDownloadCacheDirectory();
         
-        // Identify the backup participants
-        // !!! TODO: also watch package-install to keep this up to date
-        List<ResolveInfo> services = mPackageManager.queryIntentServices(
-                new Intent(BackupService.SERVICE_ACTION), 0);
-        if (DEBUG) {
-            Log.v(TAG, "Backup participants: " + services.size());
-            for (ResolveInfo ri : services) {
-                Log.v(TAG, "    " + ri + " : " + ri.filter);
-            }
+        // Build our mapping of uid to backup client services
+        synchronized (mBackupParticipants) {
+            addPackageParticipantsLocked(null);
         }
 
-        // Build our mapping of uid to backup client services
-        for (ResolveInfo ri : services) {
-            int uid = ri.serviceInfo.applicationInfo.uid;
-            HashSet<ServiceInfo> set = mBackupParticipants.get(uid);
-            if (set == null) {
-                set = new HashSet<ServiceInfo>();
-                mBackupParticipants.put(uid, set);
+        // Register for broadcasts about package install, etc., so we can
+        // update the provider list.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addDataScheme("package");
+        mContext.registerReceiver(mBroadcastReceiver, filter);
+    }
+
+    // ----- Track installation/removal of packages -----
+    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            if (DEBUG) Log.d(TAG, "Received broadcast " + intent);
+
+            Uri uri = intent.getData();
+            if (uri == null) {
+                return;
             }
-            set.add(ri.serviceInfo);
+            String pkgName = uri.getSchemeSpecificPart();
+            if (pkgName == null) {
+                return;
+            }
+
+            String action = intent.getAction();
+            if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                synchronized (mBackupParticipants) {
+                    Bundle extras = intent.getExtras();
+                    if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+                        // The package was just upgraded
+                        updatePackageParticipantsLocked(pkgName);
+                    } else {
+                        // The package was just added
+                        addPackageParticipantsLocked(pkgName);
+                    }
+                }
+            }
+            else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                Bundle extras = intent.getExtras();
+                if (extras != null && extras.getBoolean(Intent.EXTRA_REPLACING, false)) {
+                    // The package is being updated.  We'll receive a PACKAGE_ADDED shortly.
+                } else {
+                    synchronized (mBackupParticipants) {
+                        removePackageParticipantsLocked(pkgName);
+                    }
+                }
+            }
+        }
+    };
+
+    // Add the backup services in the given package to our set of known backup participants.
+    // If 'packageName' is null, adds all backup services in the system.
+    void addPackageParticipantsLocked(String packageName) {
+        List<ResolveInfo> services = mPackageManager.queryIntentServices(
+                new Intent(BackupService.SERVICE_ACTION), 0);
+        addPackageParticipantsLockedInner(packageName, services);
+    }
+
+    private void addPackageParticipantsLockedInner(String packageName, List<ResolveInfo> services) {
+        for (ResolveInfo ri : services) {
+            if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) {
+                int uid = ri.serviceInfo.applicationInfo.uid;
+                HashSet<ServiceInfo> set = mBackupParticipants.get(uid);
+                if (set == null) {
+                    set = new HashSet<ServiceInfo>();
+                    mBackupParticipants.put(uid, set);
+                }
+                if (DEBUG) {
+                    Log.v(TAG, "Adding " + services.size() + " backup participants:");
+                    for (ResolveInfo svc : services) {
+                        Log.v(TAG, "    " + svc + " : " + svc.filter);
+                    }
+                }
+
+                set.add(ri.serviceInfo);
+            }
         }
     }
 
-    
+    // Remove the given package's backup services from our known active set.  If
+    // 'packageName' is null, *all* backup services will be removed.
+    void removePackageParticipantsLocked(String packageName) {
+        List<ResolveInfo> services = mPackageManager.queryIntentServices(
+                new Intent(BackupService.SERVICE_ACTION), 0);
+        removePackageParticipantsLockedInner(packageName, services);
+    }
+
+    private void removePackageParticipantsLockedInner(String packageName, List<ResolveInfo> services) {
+        for (ResolveInfo ri : services) {
+            if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) {
+                int uid = ri.serviceInfo.applicationInfo.uid;
+                HashSet<ServiceInfo> set = mBackupParticipants.get(uid);
+                if (set != null) {
+                    set.remove(ri.serviceInfo);
+                    if (set.size() == 0) {
+                        mBackupParticipants.put(uid, null);
+                    }
+                }
+            }
+        }
+    }
+
+    // Reset the given package's known backup participants.  Unlike add/remove, the update
+    // action cannot be passed a null package name.
+    void updatePackageParticipantsLocked(String packageName) {
+        if (packageName == null) {
+            Log.e(TAG, "updatePackageParticipants called with null package name");
+            return;
+        }
+
+        // brute force but small code size
+        List<ResolveInfo> services = mPackageManager.queryIntentServices(
+                new Intent(BackupService.SERVICE_ACTION), 0);
+        removePackageParticipantsLockedInner(packageName, services);
+        addPackageParticipantsLockedInner(packageName, services);
+    }
+
     // ----- IBackupManager binder interface -----
     
     public void dataChanged(String packageName) throws RemoteException {