Expand apps' control over the settings restore process

Applications can now specify two more aspects of the restore process:  whether
they need to run with their own custom Application subclass rather than being
launched in the usual restricted mode during restore, and whether it's okay for
the backup manager to kill the app process once restore has completed.  The new
manifest attributes for these are, respectively, android:restoreNeedsApplication
and android:killAfterRestore.

If unspecified in the manifest, restoreNeedsApplication is false, and
killAfterRestore is true.

In order to support kill-after-restore cleanly, this change also adds a new
system-process-only interface to the Activity Manager, which will schedule a
"commit suicide" event on the target app's main thread looper.

The framework backup agents have been given the appropriate new backup
attributes as well.
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 1bb21b9..52d6891 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1118,6 +1118,15 @@
             mi.writeToParcel(reply, 0);
             return true;
         }
+
+        case KILL_APPLICATION_PROCESS_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String processName = data.readString();
+            int uid = data.readInt();
+            killApplicationProcess(processName, uid);
+            reply.writeNoException();
+            return true;
+        }
         }
         
         return super.onTransact(code, data, reply, flags);
@@ -2448,6 +2457,18 @@
         data.recycle();
         reply.recycle();
     }
-    
+
+    public void killApplicationProcess(String processName, int uid) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(processName);
+        data.writeInt(uid);
+        mRemote.transact(KILL_APPLICATION_PROCESS_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+        
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b4e57e0..8a26aba 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1463,6 +1463,10 @@
             queueOrSendMessage(H.EXIT_APPLICATION, null);
         }
 
+        public final void scheduleSuicide() {
+            queueOrSendMessage(H.SUICIDE, null);
+        }
+
         public void requestThumbnail(IBinder token) {
             queueOrSendMessage(H.REQUEST_THUMBNAIL, token);
         }
@@ -1752,7 +1756,8 @@
         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;
+        public static final int DESTROY_BACKUP_AGENT    = 129;
+        public static final int SUICIDE                 = 130;
         String codeToString(int code) {
             if (localLOGV) {
                 switch (code) {
@@ -1786,6 +1791,7 @@
                     case PROFILER_CONTROL: return "PROFILER_CONTROL";
                     case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT";
                     case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
+                    case SUICIDE: return "SUICIDE";
                 }
             }
             return "(unknown)";
@@ -1894,6 +1900,11 @@
                 case DESTROY_BACKUP_AGENT:
                     handleDestroyBackupAgent((CreateBackupAgentData)msg.obj);
                     break;
+                case SUICIDE:
+                    {
+                        Process.killProcess(Process.myPid());
+                    }
+                    break;
             }
         }
     }
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 15bf9ed..928981d 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -258,6 +258,13 @@
             return true;
         }
 
+        case SCHEDULE_SUICIDE_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            scheduleSuicide();
+            return true;
+        }
+
         case REQUEST_THUMBNAIL_TRANSACTION:
         {
             data.enforceInterface(IApplicationThread.descriptor);
@@ -652,7 +659,15 @@
                 IBinder.FLAG_ONEWAY);
         data.recycle();
     }
-    
+
+    public final void scheduleSuicide() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        mRemote.transact(SCHEDULE_SUICIDE_TRANSACTION, data, null,
+                IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
+
     public final void requestThumbnail(IBinder token)
             throws RemoteException {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 64daea9..98a8481 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -158,6 +158,7 @@
             throws RemoteException;
     public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException;
     public void unbindBackupAgent(ApplicationInfo appInfo) throws RemoteException;
+    public void killApplicationProcess(String processName, int uid) throws RemoteException;
     
     public boolean startInstrumentation(ComponentName className, String profileFile,
             int flags, Bundle arguments, IInstrumentationWatcher watcher)
@@ -433,4 +434,5 @@
     int KILL_APPLICATION_WITH_UID_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+95;
     int CLOSE_SYSTEM_DIALOGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+96;
     int GET_PROCESS_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+97;
+    int KILL_APPLICATION_PROCESS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+98;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index da9a957..8dda898 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -83,6 +83,7 @@
             IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode,
             Configuration config, Map<String, IBinder> services) throws RemoteException;
     void scheduleExit() throws RemoteException;
+    void scheduleSuicide() throws RemoteException;
     void requestThumbnail(IBinder token) throws RemoteException;
     void scheduleConfigurationChanged(Configuration config) throws RemoteException;
     void updateTimeZone() throws RemoteException;
@@ -133,4 +134,5 @@
     int SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
     int SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
     int GET_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+31;
+    int SCHEDULE_SUICIDE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+32;
 }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0a42a6f..8839f95 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -184,7 +184,29 @@
      * {@hide}
      */
     public static final int FLAG_ALLOW_BACKUP = 1<<14;
-    
+
+    /**
+     * Value for {@link #flags}: this is false if the application has set
+     * its android:killAfterRestore to false, true otherwise.
+     *
+     * <p>If android:allowBackup is set to false or no android:backupAgent
+     * is specified, this flag will be ignored.
+     *
+     * {@hide}
+     */
+    public static final int FLAG_KILL_AFTER_RESTORE = 1<<15;
+
+    /**
+     * Value for {@link #flags}: this is true if the application has set
+     * its android:restoreNeedsApplication to true, false otherwise.
+     *
+     * <p>If android:allowBackup is set to false or no android:backupAgent
+     * is specified, this flag will be ignored.
+     *
+     * {@hide}
+     */
+    public static final int FLAG_RESTORE_NEEDS_APPLICATION = 1<<16;
+
     /**
      * Flags associated with the application.  Any combination of
      * {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE},
@@ -193,7 +215,8 @@
      * {@link #FLAG_ALLOW_CLEAR_USER_DATA}, {@link #FLAG_UPDATED_SYSTEM_APP},
      * {@link #FLAG_TEST_ONLY}, {@link #FLAG_SUPPORTS_SMALL_SCREENS},
      * {@link #FLAG_SUPPORTS_NORMAL_SCREENS},
-     * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_RESIZEABLE_FOR_SCREENS}.
+     * {@link #FLAG_SUPPORTS_LARGE_SCREENS}, {@link #FLAG_RESIZEABLE_FOR_SCREENS},
+     * {@link #FLAG_SUPPORTS_SCREEN_DENSITIES}.
      */
     public int flags = 0;
     
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4399df4..b4a6fee 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1296,12 +1296,26 @@
                 com.android.internal.R.styleable.AndroidManifestApplication_allowBackup, true);
         if (allowBackup) {
             ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
+
+            // backupAgent, killAfterRestore, and restoreNeedsApplication are only relevant
+            // if backup is possible for the given application.
             String backupAgent = sa.getNonResourceString(
                     com.android.internal.R.styleable.AndroidManifestApplication_backupAgent);
             if (backupAgent != null) {
                 ai.backupAgentName = buildClassName(pkgName, backupAgent, outError);
                 Log.v(TAG, "android:backupAgent = " + ai.backupAgentName
                         + " from " + pkgName + "+" + backupAgent);
+
+                if (sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestApplication_killAfterRestore,
+                        true)) {
+                    ai.flags |= ApplicationInfo.FLAG_KILL_AFTER_RESTORE;
+                }
+                if (sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestApplication_restoreNeedsApplication,
+                        false)) {
+                    ai.flags |= ApplicationInfo.FLAG_RESTORE_NEEDS_APPLICATION;
+                }
             }
         }
         
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1ea5fa3..53e0125 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1113,6 +1113,7 @@
                  android:label="@string/android_system_label"
                  android:allowClearUserData="false"
                  android:backupAgent="com.android.server.SystemBackupAgent"
+                 android:killAfterRestore="false"
                  android:icon="@drawable/ic_launcher_android">
         <activity android:name="com.android.internal.app.ChooserActivity"
                 android:theme="@style/Theme.Dialog.Alert"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index ce421db..7aaf218 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -580,6 +580,15 @@
     <!-- This is not the attribute you are looking for. -->
     <attr name="allowBackup" format="boolean" />
 
+    <!-- Whether the application in question should be terminated after its
+         settings have been restored.  The default is to do so. -->
+    <attr name="killAfterRestore" format="boolean" />
+
+    <!-- Whether the application needs to have its own Application subclass
+         active during restore.  The default is to run restore with a minimal
+         Application class to avoid interference with application logic. -->
+    <attr name="restoreNeedsApplication" format="boolean" />
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -656,6 +665,8 @@
         <attr name="testOnly" />
         <attr name="backupAgent" />
         <attr name="allowBackup" />
+        <attr name="killAfterRestore" />
+        <attr name="restoreNeedsApplication" />
     </declare-styleable>
     
     <!-- The <code>permission</code> tag declares a security permission that can be
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0fba0f6..1a362f7 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1155,10 +1155,11 @@
   <public type="attr" name="wallpaperActivityOpenExitAnimation" />
   <public type="attr" name="wallpaperActivityCloseEnterAnimation" />
   <public type="attr" name="wallpaperActivityCloseExitAnimation" />
+  <public type="attr" name="supportsUploading" />
+  <public type="attr" name="killAfterRestore" />
+  <public type="attr" name="restoreNeedsApplication" />
 
   <public type="style" name="Theme.Wallpaper" />
   <public type="style" name="Theme.Wallpaper.NoTitleBar" />
   <public type="style" name="Theme.Wallpaper.NoTitleBar.Fullscreen" />
-
-  <public type="attr" name="supportsUploading" />
 </resources>