Retool the backup process to use a new 'BackupAgent' class

Backups will be handled by launching the application in a special
mode under which no activities or services will be started, only
the BackupAgent subclass named in the app's android:backupAgent
manifest property.  This takes the place of the BackupService class
used earlier during development.

In the cases of *full* backup or restore, an application that does
not supply its own BackupAgent will be launched in a restricted
manner; in particular, it will be using the default Application
class rather than any manifest-declared one.  This ensures that the
app is not running any code that may try to manipulate its data
while the backup system reads/writes its data set.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 06e0a453..29e57cd 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -115,6 +115,7 @@
     private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
     private static final boolean DEBUG_BROADCAST = false;
     private static final boolean DEBUG_RESULTS = false;
+    private static final boolean DEBUG_BACKUP = true;
     private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
     private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
     private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
@@ -499,7 +500,7 @@
             return mResources;
         }
 
-        public Application makeApplication() {
+        public Application makeApplication(boolean forceDefaultAppClass) {
             if (mApplication != null) {
                 return mApplication;
             }
@@ -507,7 +508,7 @@
             Application app = null;
             
             String appClass = mApplicationInfo.className;
-            if (appClass == null) {
+            if (forceDefaultAppClass || (appClass == null)) {
                 appClass = "android.app.Application";
             }
 
@@ -1199,6 +1200,16 @@
         }
     }
 
+    private static final class CreateBackupAgentData {
+        ApplicationInfo appInfo;
+        int backupMode;
+        public String toString() {
+            return "CreateBackupAgentData{appInfo=" + appInfo
+                    + " backupAgent=" + appInfo.backupAgentName
+                    + " mode=" + backupMode + "}";
+        }
+    }
+    
     private static final class CreateServiceData {
         IBinder token;
         ServiceInfo info;
@@ -1239,6 +1250,7 @@
         Bundle instrumentationArgs;
         IInstrumentationWatcher instrumentationWatcher;
         int debugMode;
+        boolean restrictedBackupMode;
         Configuration config;
         boolean handlingProfiling;
         public String toString() {
@@ -1374,6 +1386,21 @@
             queueOrSendMessage(H.RECEIVER, r);
         }
 
+        public final void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) {
+            CreateBackupAgentData d = new CreateBackupAgentData();
+            d.appInfo = app;
+            d.backupMode = backupMode;
+
+            queueOrSendMessage(H.CREATE_BACKUP_AGENT, d);
+        }
+
+        public final void scheduleDestroyBackupAgent(ApplicationInfo app) {
+            CreateBackupAgentData d = new CreateBackupAgentData();
+            d.appInfo = app;
+
+            queueOrSendMessage(H.DESTROY_BACKUP_AGENT, d);
+        }
+
         public final void scheduleCreateService(IBinder token,
                 ServiceInfo info) {
             CreateServiceData s = new CreateServiceData();
@@ -1419,7 +1446,7 @@
                 ApplicationInfo appInfo, List<ProviderInfo> providers,
                 ComponentName instrumentationName, String profileFile,
                 Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
-                int debugMode, Configuration config,
+                int debugMode, boolean isRestrictedBackupMode, Configuration config,
                 Map<String, IBinder> services) {
             Process.setArgV0(processName);
 
@@ -1437,6 +1464,7 @@
             data.instrumentationArgs = instrumentationArgs;
             data.instrumentationWatcher = instrumentationWatcher;
             data.debugMode = debugMode;
+            data.restrictedBackupMode = isRestrictedBackupMode;
             data.config = config;
             queueOrSendMessage(H.BIND_APPLICATION, data);
         }
@@ -1718,6 +1746,8 @@
         public static final int ACTIVITY_CONFIGURATION_CHANGED = 125;
         public static final int RELAUNCH_ACTIVITY       = 126;
         public static final int PROFILER_CONTROL        = 127;
+        public static final int CREATE_BACKUP_AGENT     = 128;
+        public static final int DESTROY_BACKUP_AGENT     = 129;
         String codeToString(int code) {
             if (localLOGV) {
                 switch (code) {
@@ -1749,6 +1779,8 @@
                     case ACTIVITY_CONFIGURATION_CHANGED: return "ACTIVITY_CONFIGURATION_CHANGED";
                     case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
                     case PROFILER_CONTROL: return "PROFILER_CONTROL";
+                    case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT";
+                    case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
                 }
             }
             return "(unknown)";
@@ -1851,6 +1883,12 @@
                 case PROFILER_CONTROL:
                     handleProfilerControl(msg.arg1 != 0, (String)msg.obj);
                     break;
+                case CREATE_BACKUP_AGENT:
+                    handleCreateBackupAgent((CreateBackupAgentData)msg.obj);
+                    break;
+                case DESTROY_BACKUP_AGENT:
+                    handleDestroyBackupAgent((CreateBackupAgentData)msg.obj);
+                    break;
             }
         }
     }
@@ -1908,6 +1946,8 @@
     Application mInitialApplication;
     final ArrayList<Application> mAllApplications
             = new ArrayList<Application>();
+    // set of instantiated backup agents, keyed by package name
+    final HashMap<String, BackupAgent> mBackupAgents = new HashMap<String, BackupAgent>();
     static final ThreadLocal sThreadLocal = new ThreadLocal();
     Instrumentation mInstrumentation;
     String mInstrumentationAppDir = null;
@@ -2269,7 +2309,7 @@
         }
 
         try {
-            Application app = r.packageInfo.makeApplication();
+            Application app = r.packageInfo.makeApplication(false);
             
             if (localLOGV) Log.v(TAG, "Performing launch of " + r);
             if (localLOGV) Log.v(
@@ -2464,7 +2504,7 @@
         }
 
         try {
-            Application app = packageInfo.makeApplication();
+            Application app = packageInfo.makeApplication(false);
             
             if (localLOGV) Log.v(
                 TAG, "Performing receive of " + data.intent
@@ -2507,6 +2547,85 @@
         }
     }
 
+    // Instantiate a BackupAgent and tell it that it's alive
+    private final void handleCreateBackupAgent(CreateBackupAgentData data) {
+        if (DEBUG_BACKUP) Log.v(TAG, "handleCreateBackupAgent: " + data);
+
+        // no longer idle; we have backup work to do
+        unscheduleGcIdler();
+
+        // instantiate the BackupAgent class named in the manifest
+        PackageInfo packageInfo = getPackageInfoNoCheck(data.appInfo);
+        String packageName = packageInfo.mPackageName;
+        if (mBackupAgents.get(packageName) != null) {
+            Log.d(TAG, "BackupAgent " + "  for " + packageName
+                    + " already exists");
+            return;
+        }
+        
+        BackupAgent agent = null;
+        String classname = data.appInfo.backupAgentName;
+        if (classname == null) {
+            if (data.backupMode == IApplicationThread.BACKUP_MODE_INCREMENTAL) {
+                Log.e(TAG, "Attempted incremental backup but no defined agent for "
+                        + packageName);
+                return;
+            }
+            classname = "android.app.FullBackupAgent";
+        }
+        try {
+            java.lang.ClassLoader cl = packageInfo.getClassLoader();
+            agent = (BackupAgent) cl.loadClass(data.appInfo.backupAgentName).newInstance();
+        } catch (Exception e) {
+            throw new RuntimeException("Unable to instantiate backup agent "
+                    + data.appInfo.backupAgentName + ": " + e.toString(), e);
+        }
+        
+        // set up the agent's context
+        try {
+            if (DEBUG_BACKUP) Log.v(TAG, "Initializing BackupAgent "
+                    + data.appInfo.backupAgentName);
+            
+            ApplicationContext context = new ApplicationContext();
+            context.init(packageInfo, null, this);
+            context.setOuterContext(agent);
+            agent.attach(context);
+            agent.onCreate();
+
+            // tell the OS that we're live now
+            IBinder binder = agent.onBind();
+            try {
+                ActivityManagerNative.getDefault().backupAgentCreated(packageName, binder);
+            } catch (RemoteException e) {
+                // nothing to do.
+            }
+            mBackupAgents.put(packageName, agent);
+        } catch (Exception e) {
+            throw new RuntimeException("Unable to create BackupAgent "
+                    + data.appInfo.backupAgentName + ": " + e.toString(), e);
+        }
+    }
+
+    // Tear down a BackupAgent
+    private final void handleDestroyBackupAgent(CreateBackupAgentData data) {
+        if (DEBUG_BACKUP) Log.v(TAG, "handleDestroyBackupAgent: " + data);
+        
+        PackageInfo packageInfo = getPackageInfoNoCheck(data.appInfo);
+        String packageName = packageInfo.mPackageName;
+        BackupAgent agent = mBackupAgents.get(packageName);
+        if (agent != null) {
+            try {
+                agent.onDestroy();
+            } catch (Exception e) {
+                Log.w(TAG, "Exception thrown in onDestroy by backup agent of " + data.appInfo);
+                e.printStackTrace();
+            }
+            mBackupAgents.remove(packageName);
+        } else {
+            Log.w(TAG, "Attempt to destroy unknown backup agent " + data);
+        }
+    }
+
     private final void handleCreateService(CreateServiceData data) {
         // If we are getting ready to gc after going to the background, well
         // we are back active so skip it.
@@ -2532,7 +2651,7 @@
             ApplicationContext context = new ApplicationContext();
             context.init(packageInfo, null, this);
 
-            Application app = packageInfo.makeApplication();
+            Application app = packageInfo.makeApplication(false);
             context.setOuterContext(service);
             service.attach(context, this, data.info.name, data.token, app,
                     ActivityManagerNative.getDefault());
@@ -3694,7 +3813,9 @@
             mInstrumentation = new Instrumentation();
         }
 
-        Application app = data.info.makeApplication();
+        // If the app is being launched for full backup or restore, bring it up in
+        // a restricted environment with the base application class.
+        Application app = data.info.makeApplication(data.restrictedBackupMode);
         mInitialApplication = app;
 
         List<ProviderInfo> providers = data.providers;