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/Android.mk b/Android.mk
index 4df6f8e..26bcb5c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -70,6 +70,7 @@
 	core/java/android/app/IActivityPendingResult.aidl \
 	core/java/android/app/IActivityWatcher.aidl \
 	core/java/android/app/IAlarmManager.aidl \
+    core/java/android/app/IBackupAgent.aidl \
 	core/java/android/app/IInstrumentationWatcher.aidl \
 	core/java/android/app/IIntentReceiver.aidl \
 	core/java/android/app/IIntentSender.aidl \
@@ -82,7 +83,6 @@
 	core/java/android/app/IWallpaperService.aidl \
 	core/java/android/app/IWallpaperServiceCallback.aidl \
 	core/java/android/backup/IBackupManager.aidl \
-	core/java/android/backup/IBackupService.aidl \
 	core/java/android/bluetooth/IBluetoothA2dp.aidl \
 	core/java/android/bluetooth/IBluetoothDevice.aidl \
 	core/java/android/bluetooth/IBluetoothDeviceCallback.aidl \
diff --git a/api/current.xml b/api/current.xml
index f572a4d23..3e22a13 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -2077,6 +2077,17 @@
  visibility="public"
 >
 </field>
+<field name="allowBackup"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843393"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="allowClearUserData"
  type="int"
  transient="false"
@@ -2308,6 +2319,17 @@
  visibility="public"
 >
 </field>
+<field name="backupAgent"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843392"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="baselineAlignBottom"
  type="int"
  transient="false"
@@ -3507,28 +3529,6 @@
  visibility="public"
 >
 </field>
-<field name="donut_resource_pad31"
- type="int"
- transient="false"
- volatile="false"
- value="16843393"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
-<field name="donut_resource_pad32"
- type="int"
- transient="false"
- volatile="false"
- value="16843392"
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 <field name="donut_resource_pad4"
  type="int"
  transient="false"
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 16f0a30..3d3d7d5f 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -20,6 +20,7 @@
 import android.content.ContentResolver;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.res.Configuration;
@@ -1021,6 +1022,33 @@
             reply.writeStrongBinder(binder);
             return true;
         }
+        
+        case START_BACKUP_AGENT_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            ApplicationInfo info = ApplicationInfo.CREATOR.createFromParcel(data);
+            int backupRestoreMode = data.readInt();
+            boolean success = bindBackupAgent(info, backupRestoreMode);
+            reply.writeNoException();
+            reply.writeInt(success ? 1 : 0);
+            return true;
+        }
+
+        case BACKUP_AGENT_CREATED_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            String packageName = data.readString();
+            IBinder agent = data.readStrongBinder();
+            backupAgentCreated(packageName, agent);
+            reply.writeNoException();
+            return true;
+        }
+
+        case UNBIND_BACKUP_AGENT_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            ApplicationInfo info = ApplicationInfo.CREATOR.createFromParcel(data);
+            unbindBackupAgent(info);
+            reply.writeNoException();
+            return true;
+        }
         }
         
         return super.onTransact(code, data, reply, flags);
@@ -1681,6 +1709,43 @@
         return binder;
     }
 
+    public boolean bindBackupAgent(ApplicationInfo app, int backupRestoreMode)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        app.writeToParcel(data, 0);
+        data.writeInt(backupRestoreMode);
+        mRemote.transact(START_BACKUP_AGENT_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean success = reply.readInt() != 0;
+        reply.recycle();
+        data.recycle();
+        return success;
+    }
+
+    public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(packageName);
+        data.writeStrongBinder(agent);
+        mRemote.transact(BACKUP_AGENT_CREATED_TRANSACTION, data, reply, 0);
+        reply.recycle();
+        data.recycle();
+    }
+
+    public void unbindBackupAgent(ApplicationInfo app) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        app.writeToParcel(data, 0);
+        mRemote.transact(UNBIND_BACKUP_AGENT_TRANSACTION, data, reply, 0);
+        reply.readException();
+        reply.recycle();
+        data.recycle();
+    }
+
     public boolean startInstrumentation(ComponentName className, String profileFile,
             int flags, Bundle arguments, IInstrumentationWatcher watcher)
             throws RemoteException {
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;
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index f243185..e28fd0f 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -230,11 +230,13 @@
             IBinder binder = data.readStrongBinder();
             IInstrumentationWatcher testWatcher = IInstrumentationWatcher.Stub.asInterface(binder);
             int testMode = data.readInt();
+            boolean restrictedBackupMode = (data.readInt() != 0);
             Configuration config = Configuration.CREATOR.createFromParcel(data);
             HashMap<String, IBinder> services = data.readHashMap(null);
             bindApplication(packageName, info,
                             providers, testName, profileName,
-                            testArgs, testWatcher, testMode, config, services);
+                            testArgs, testWatcher, testMode, restrictedBackupMode,
+                            config, services);
             return true;
         }
         
@@ -339,6 +341,15 @@
             setSchedulingGroup(group);
             return true;
         }
+
+        case SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data);
+            int backupMode = data.readInt();
+            scheduleCreateBackupAgent(appInfo, backupMode);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -492,6 +503,24 @@
         data.recycle();
     }
 
+    public final void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        app.writeToParcel(data, 0);
+        data.writeInt(backupMode);
+        mRemote.transact(SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION, data, null, 0);
+        data.recycle();
+    }
+
+    public final void scheduleDestroyBackupAgent(ApplicationInfo app) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        app.writeToParcel(data, 0);
+        mRemote.transact(SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION, data, null, 0);
+        data.recycle();
+    }
+    
     public final void scheduleCreateService(IBinder token, ServiceInfo info)
             throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -551,7 +580,8 @@
     public final void bindApplication(String packageName, ApplicationInfo info,
             List<ProviderInfo> providers, ComponentName testName,
             String profileName, Bundle testArgs, IInstrumentationWatcher testWatcher, int debugMode,
-            Configuration config, Map<String, IBinder> services) throws RemoteException {
+            boolean restrictedBackupMode, Configuration config,
+            Map<String, IBinder> services) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeString(packageName);
@@ -567,6 +597,7 @@
         data.writeBundle(testArgs);
         data.writeStrongInterface(testWatcher);
         data.writeInt(debugMode);
+        data.writeInt(restrictedBackupMode ? 1 : 0);
         config.writeToParcel(data, 0);
         data.writeMap(services);
         mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
diff --git a/core/java/android/backup/BackupService.java b/core/java/android/app/BackupAgent.java
similarity index 66%
rename from core/java/android/backup/BackupService.java
rename to core/java/android/app/BackupAgent.java
index 50a5921..997bfdc 100644
--- a/core/java/android/backup/BackupService.java
+++ b/core/java/android/app/BackupAgent.java
@@ -14,13 +14,12 @@
  * limitations under the License.
  */
 
-package android.backup;
+package android.app;
 
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.app.Service;
-import android.backup.IBackupService;
-import android.content.Intent;
+import android.app.IBackupAgent;
+import android.backup.BackupDataOutput;
+import android.content.Context;
+import android.content.ContextWrapper;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
@@ -30,31 +29,18 @@
  * This is the central interface between an application and Android's
  * settings backup mechanism.
  * 
- * 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 BackupService#SERVICE_ACTION} intent action.  For example:
- * 
- * <pre class="prettyprint">
- *      &lt;!-- Use the class "MyBackupService" to perform backups for my app --&gt;
- *      &lt;service android:name=".MyBackupService"&gt;
- *          &lt;intent-filter&gt;
- *              &lt;action android:name="android.backup.BackupService.SERVICE" /&gt;
- *          &lt;/intent-filter&gt;
- *      &lt;/service&gt;</pre>
- * 
  * @hide pending API solidification
  */
+public abstract class BackupAgent extends ContextWrapper {
+    public BackupAgent() {
+        super(null);
+    }
 
-public abstract class BackupService extends Service {
-    /**
-     * Service Action: Participate in the backup infrastructure.  Applications
-     * that wish to use the Android backup mechanism must provide an exported
-     * subclass of BackupService and give it an {@link android.content.IntentFilter
-     * IntentFilter} that accepts this action. 
-     */
-    @SdkConstant(SdkConstantType.SERVICE_ACTION)
-    public static final String SERVICE_ACTION = "android.backup.BackupService.SERVICE";
+    public void onCreate() {
+    }
+
+    public void onDestroy() {
+    }
 
     /**
      * The application is being asked to write any data changed since the
@@ -91,7 +77,8 @@
      *                 file.  The application should record the final backup state
      *                 here after restoring its data from dataFd.
      */
-    public abstract void onRestore(ParcelFileDescriptor /* TODO: BackupDataInput */ data, ParcelFileDescriptor newState);
+    public abstract void onRestore(ParcelFileDescriptor /* TODO: BackupDataInput */ data,
+            ParcelFileDescriptor newState);
 
 
     // ----- Core implementation -----
@@ -100,29 +87,33 @@
      * Returns the private interface called by the backup system.  Applications will
      * not typically override this.
      */
-    public IBinder onBind(Intent intent) {
-        if (intent.getAction().equals(SERVICE_ACTION)) {
-            return mBinder;
-        }
-        return null;
+    public IBinder onBind() {
+        return mBinder;
     }
 
     private final IBinder mBinder = new BackupServiceBinder().asBinder();
 
+    /** @hide */
+    public void attach(Context context) {
+        attachBaseContext(context);
+    }
+
     // ----- IBackupService binder interface -----
-    private class BackupServiceBinder extends IBackupService.Stub {
+    private class BackupServiceBinder extends IBackupAgent.Stub {
+        private static final String TAG = "BackupServiceBinder";
+
         public void doBackup(ParcelFileDescriptor oldState,
                 ParcelFileDescriptor data,
                 ParcelFileDescriptor newState) throws RemoteException {
             // !!! TODO - real implementation; for now just invoke the callbacks directly
-            Log.v("BackupServiceBinder", "doBackup() invoked");
-            BackupDataOutput output = new BackupDataOutput(BackupService.this,
+            Log.v(TAG, "doBackup() invoked");
+            BackupDataOutput output = new BackupDataOutput(BackupAgent.this,
                     data.getFileDescriptor());
             try {
-                BackupService.this.onBackup(oldState, output, newState);
+                BackupAgent.this.onBackup(oldState, output, newState);
             } catch (RuntimeException ex) {
-                Log.d("BackupService", "onBackup ("
-                        + BackupService.this.getClass().getName() + ") threw", ex);
+                Log.d("BackupAgent", "onBackup ("
+                        + BackupAgent.this.getClass().getName() + ") threw", ex);
                 throw ex;
             }
         }
@@ -130,8 +121,8 @@
         public void doRestore(ParcelFileDescriptor data,
                 ParcelFileDescriptor newState) throws RemoteException {
             // !!! TODO - real implementation; for now just invoke the callbacks directly
-            Log.v("BackupServiceBinder", "doRestore() invoked");
-            BackupService.this.onRestore(data, newState);
+            Log.v(TAG, "doRestore() invoked");
+            BackupAgent.this.onRestore(data, newState);
         }
     }
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index d15a154..c948aec 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -21,6 +21,7 @@
 import android.content.IContentProvider;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.ProviderInfo;
@@ -149,6 +150,11 @@
     public void serviceDoneExecuting(IBinder token) throws RemoteException;
     public IBinder peekService(Intent service, String resolvedType) throws RemoteException;
     
+    public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode)
+            throws RemoteException;
+    public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException;
+    public void unbindBackupAgent(ApplicationInfo appInfo) throws RemoteException;
+    
     public boolean startInstrumentation(ComponentName className, String profileFile,
             int flags, Bundle arguments, IInstrumentationWatcher watcher)
             throws RemoteException;
@@ -397,4 +403,7 @@
     int SHUTDOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+86;
     int STOP_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+87;
     int RESUME_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+88;
+    int START_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+89;
+    int BACKUP_AGENT_CREATED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+90;
+    int UNBIND_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+91;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index ec03d3a..bca1fea 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -59,6 +59,11 @@
             int configChanges) throws RemoteException;
     void scheduleReceiver(Intent intent, ActivityInfo info, int resultCode,
             String data, Bundle extras, boolean sync) throws RemoteException;
+    static final int BACKUP_MODE_INCREMENTAL = 0;
+    static final int BACKUP_MODE_FULL = 1;
+    static final int BACKUP_MODE_RESTORE = 2;
+    void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) throws RemoteException;
+    void scheduleDestroyBackupAgent(ApplicationInfo app) throws RemoteException;
     void scheduleCreateService(IBinder token, ServiceInfo info) throws RemoteException;
     void scheduleBindService(IBinder token,
             Intent intent, boolean rebind) throws RemoteException;
@@ -71,8 +76,8 @@
     static final int DEBUG_WAIT = 2;
     void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers,
             ComponentName testName, String profileName, Bundle testArguments, 
-            IInstrumentationWatcher testWatcher, int debugMode, Configuration config, Map<String,
-            IBinder> services) throws RemoteException;
+            IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode,
+            Configuration config, Map<String, IBinder> services) throws RemoteException;
     void scheduleExit() throws RemoteException;
     void requestThumbnail(IBinder token) throws RemoteException;
     void scheduleConfigurationChanged(Configuration config) throws RemoteException;
@@ -119,4 +124,6 @@
     int REQUEST_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+26;
     int PROFILER_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27;
     int SET_SCHEDULING_GROUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
+    int SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
+    int SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
 }
diff --git a/core/java/android/backup/IBackupService.aidl b/core/java/android/app/IBackupAgent.aidl
similarity index 94%
rename from core/java/android/backup/IBackupService.aidl
rename to core/java/android/app/IBackupAgent.aidl
index 1bde8ea..bb9f008 100644
--- a/core/java/android/backup/IBackupService.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -14,18 +14,18 @@
  * limitations under the License.
  */
 
-package android.backup;
+package android.app;
 
 import android.os.ParcelFileDescriptor;
  
 /**
  * Interface presented by applications being asked to participate in the
  * backup & restore mechanism.  End user code does not typically implement
- * this interface; they subclass BackupService instead.
+ * this interface; they subclass BackupAgent instead.
  *
  * {@hide}
  */ 
-interface IBackupService {
+interface IBackupAgent {
     /**
      * Request that the app perform an incremental backup.
      *
diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl
index cf22798..3468d70 100644
--- a/core/java/android/backup/IBackupManager.aidl
+++ b/core/java/android/backup/IBackupManager.aidl
@@ -34,8 +34,22 @@
     oneway void dataChanged(String packageName);
 
     /**
+     * Notifies the Backup Manager Service that an agent has become available.  This
+     * method is only invoked by the Activity Manager.
+     * !!! TODO: permission
+     */
+    oneway void agentConnected(String packageName, IBinder agent);
+
+    /**
+     * Notify the Backup Manager Service that an agent has unexpectedly gone away.
+     * This method is only invoked by the Activity Manager.
+     * !!! TODO: permission
+     */
+    oneway void agentDisconnected(String packageName);
+
+    /**
      * Schedule a full backup of the given package.
-     * !!! TODO: protect with a signature-or-system permission?
+     * !!! TODO: permission
      */
     oneway void scheduleFullBackup(String packageName);
 }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index ad022e7..f16eb74 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -58,11 +58,22 @@
      * Class implementing the Application's manage space
      * functionality.  From the "manageSpaceActivity"
      * attribute. This is an optional attribute and will be null if
-     * application's dont specify it in their manifest
+     * applications don't specify it in their manifest
      */
     public String manageSpaceActivityName;    
     
     /**
+     * Class implementing the Application's backup functionality.  From
+     * the "backupAgent" attribute.  This is an optional attribute and
+     * will be null if the application does not specify it in its manifest.
+     * 
+     * <p>If android:allowBackup is set to false, this attribute is ignored.
+     * 
+     * {@hide}
+     */
+    public String backupAgentName;
+    
+    /**
      * Value for {@link #flags}: if set, this application is installed in the
      * device's system image.
      */
@@ -93,7 +104,7 @@
     public static final int FLAG_PERSISTENT = 1<<3;
 
     /**
-     * Value for {@link #flags}: set to true iif this application holds the
+     * Value for {@link #flags}: set to true if this application holds the
      * {@link android.Manifest.permission#FACTORY_TEST} permission and the
      * device is running in factory test mode.
      */
@@ -126,6 +137,14 @@
     public static final int FLAG_TEST_ONLY = 1<<8;
 
     /**
+     * Value for {@link #flags}: this is false if the application has set
+     * its android:allowBackup to false, true otherwise.
+     * 
+     * {@hide}
+     */
+    public static final int FLAG_ALLOW_BACKUP = 1<<10;
+    
+    /**
      * Flags associated with the application.  Any combination of
      * {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE},
      * {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and
@@ -285,6 +304,7 @@
         dest.writeInt(targetSdkVersion);
         dest.writeInt(enabled ? 1 : 0);
         dest.writeString(manageSpaceActivityName);
+        dest.writeString(backupAgentName);
         dest.writeInt(descriptionRes);
         dest.writeIntArray(supportsDensities);
     }
@@ -315,6 +335,7 @@
         targetSdkVersion = source.readInt();
         enabled = source.readInt() != 0;
         manageSpaceActivityName = source.readString();
+        backupAgentName = source.readString();
         descriptionRes = source.readInt();
         supportsDensities = source.createIntArray();
     }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ee20aee..36d154e 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1176,6 +1176,19 @@
                     outError);
         }
 
+        boolean allowBackup = sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_allowBackup, true);
+        if (allowBackup) {
+            ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
+            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);
+            }
+        }
+        
         TypedValue v = sa.peekValue(
                 com.android.internal.R.styleable.AndroidManifestApplication_label);
         if (v != null && (ai.labelRes=v.resourceId) == 0) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 36f7dfb..b105aaa 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -972,6 +972,13 @@
         android:description="@string/permdesc_batteryStats"
         android:protectionLevel="normal" />
 
+    <!-- Allows an application to control the backup and restore process
+         @hide pending API council -->
+    <permission android:name="android.permission.BACKUP"
+        android:label="@string/permlab_backup"
+        android:description="@string/permdesc_backup"
+        android:protectionLevel="signature" />
+
     <!-- Allows an application to tell the AppWidget service which application
          can access AppWidget's data.  The normal user flow is that a user
          picks an AppWidget to go into a particular host, thereby giving that
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 7b48267..817a566 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -570,6 +570,15 @@
     <!-- Application's requirement for five way navigation -->
     <attr name="reqFiveWayNav" format="boolean" />
 
+    <!-- The name of the class implementing <code>BackupAgent</code> to manage
+         backup and restore of the application's settings to external storage. -->
+    <attr name="backupAgent" format="string" />
+
+    <!-- Whether the application allows its data to be backed up at all.  This
+         attribute defaults to 'true':  unless the application opts out, the
+         user will be able to back up its data to desktop storage. -->
+    <attr name="allowBackup" 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
@@ -643,6 +652,8 @@
         <attr name="manageSpaceActivity" />
         <attr name="allowClearUserData" />
         <attr name="testOnly" />
+        <attr name="backupAgent" />
+        <attr name="allowBackup" />
     </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 621270e..c2c84d6 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1113,7 +1113,9 @@
   <public type="attr" name="gestureStrokeAngleThreshold" />
   <public type="attr" name="eventsInterceptionEnabled" />
   <public type="attr" name="fadeEnabled" />
-
+  <public type="attr" name="backupAgent" />
+  <public type="attr" name="allowBackup" />
+  
   <public-padding type="attr" name="donut_resource_pad" end="0x0101029f" />
 
   <public-padding type="id" name="donut_resource_pad" end="0x01020040" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e311abd..7078bbf 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -540,6 +540,11 @@
         collected battery statistics. Not for use by normal applications.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_backup">control system backup and restore</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_backup">Allows the application to control the system's backup and restore mechanism.  Not for use by normal applications.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_internalSystemWindow">display unauthorized windows</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_internalSystemWindow">Allows the creation of
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index e3fff81..82ed1e3 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -16,17 +16,16 @@
 
 package com.android.server;
 
-import android.backup.BackupService;
-import android.backup.IBackupService;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.IApplicationThread;
+import android.app.IBackupAgent;
 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.ApplicationInfo;
 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;
@@ -49,6 +48,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 
 class BackupManagerService extends IBackupManager.Stub {
@@ -62,22 +62,28 @@
     
     private Context mContext;
     private PackageManager mPackageManager;
+    private final IActivityManager mActivityManager;
     private final BackupHandler mBackupHandler = new BackupHandler();
     // map UIDs to the set of backup client services within that UID's app set
-    private SparseArray<HashSet<ServiceInfo>> mBackupParticipants
-        = new SparseArray<HashSet<ServiceInfo>>();
+    private SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
+        = new SparseArray<HashSet<ApplicationInfo>>();
     // set of backup services that have pending changes
     private class BackupRequest {
-        public ServiceInfo service;
+        public ApplicationInfo appInfo;
         public boolean fullBackup;
         
-        BackupRequest(ServiceInfo svc, boolean isFull) {
-            service = svc;
+        BackupRequest(ApplicationInfo app, boolean isFull) {
+            appInfo = app;
             fullBackup = isFull;
         }
+
+        public String toString() {
+            return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}";
+        }
     }
     // Backups that we haven't started yet.
-    private HashMap<ComponentName,BackupRequest> mPendingBackups = new HashMap();
+    private HashMap<ApplicationInfo,BackupRequest> mPendingBackups
+            = new HashMap<ApplicationInfo,BackupRequest>();
     // Backups that we have started.  These are separate to prevent starvation
     // if an app keeps re-enqueuing itself.
     private ArrayList<BackupRequest> mBackupQueue;
@@ -89,6 +95,7 @@
     public BackupManagerService(Context context) {
         mContext = context;
         mPackageManager = context.getPackageManager();
+        mActivityManager = ActivityManagerNative.getDefault();
 
         // Set up our bookkeeping
         mStateDir = new File(Environment.getDataDirectory(), "backup");
@@ -151,7 +158,7 @@
 
     // ----- Run the actual backup process asynchronously -----
 
-    private class BackupHandler extends Handler implements ServiceConnection {
+    private class BackupHandler extends Handler {
         public void handleMessage(Message msg) {
 
             switch (msg.what) {
@@ -163,31 +170,20 @@
                         for (BackupRequest b: mPendingBackups.values()) {
                             mBackupQueue.add(b);
                         }
-                        mPendingBackups = new HashMap<ComponentName,BackupRequest>();
+                        mPendingBackups = new HashMap<ApplicationInfo,BackupRequest>();
                     }
                     // !!! TODO: start a new backup-queue journal file too
                     // WARNING: If we crash after this line, anything in mPendingBackups will
                     // be lost.  FIX THIS.
                 }
-                startOneService();
+                startOneAgent();
                 break;
             }
         }
-        
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            Log.d(TAG, "onServiceConnected name=" + name + " service=" + service);
-            IBackupService bs = IBackupService.Stub.asInterface(service);
-            processOneBackup(name, bs);
-        }
-
-        public void onServiceDisconnected(ComponentName name) {
-            // TODO: handle backup being interrupted
-        }
     }
 
-    void startOneService() {
+    void startOneAgent() {
         // Loop until we find someone to start or the queue empties out.
-        Intent intent = new Intent(BackupService.SERVICE_ACTION);
         while (true) {
             BackupRequest request;
             synchronized (mQueueLock) {
@@ -205,14 +201,19 @@
                 // Take it off the queue when we're done.
             }
             
-            intent.setClassName(request.service.packageName, request.service.name);
-            Log.d(TAG, "binding to " + intent);
+            Log.d(TAG, "starting agent for " + request);
+            // !!! TODO: need to handle the restore case?
+            int mode = (request.fullBackup)
+                    ? IApplicationThread.BACKUP_MODE_FULL
+                    : IApplicationThread.BACKUP_MODE_INCREMENTAL;
             try {
-                if (mContext.bindService(intent, mBackupHandler, Context.BIND_AUTO_CREATE)) {
-                    Log.d(TAG, "awaiting service object for " + intent);
+                if (mActivityManager.bindBackupAgent(request.appInfo, mode)) {
+                    Log.d(TAG, "awaiting agent for " + request);
                     // success
                     return;
                 }
+            } catch (RemoteException e) {
+                // can't happen; activity manager is local
             } catch (SecurityException ex) {
                 // Try for the next one.
                 Log.d(TAG, "error in bind", ex);
@@ -220,23 +221,23 @@
         }
     }
 
-    void processOneBackup(ComponentName name, IBackupService bs) {
-        try {
-            Log.d(TAG, "processOneBackup doBackup() on " + name);
+    void processOneBackup(String packageName, IBackupAgent bs) {
+        Log.d(TAG, "processOneBackup doBackup() on " + packageName);
 
-            BackupRequest request;
-            synchronized (mQueueLock) {
-                if (mBackupQueue == null) {
-                    Log.d(TAG, "mBackupQueue is null.  WHY?");
-                }
-                request = mBackupQueue.get(0);
+        BackupRequest request;
+        synchronized (mQueueLock) {
+            if (mBackupQueue == null) {
+                Log.d(TAG, "mBackupQueue is null.  WHY?");
             }
+            request = mBackupQueue.get(0);
+        }
 
+        try {
             // !!! TODO right now these naming schemes limit applications to
             // one backup service per package
-            File savedStateName = new File(mStateDir, request.service.packageName);
-            File backupDataName = new File(mDataDir, request.service.packageName + ".data");
-            File newStateName = new File(mStateDir, request.service.packageName + ".new");
+            File savedStateName = new File(mStateDir, request.appInfo.packageName);
+            File backupDataName = new File(mDataDir, request.appInfo.packageName + ".data");
+            File newStateName = new File(mStateDir, request.appInfo.packageName + ".new");
             
             // In a full backup, we pass a null ParcelFileDescriptor as
             // the saved-state "file"
@@ -280,7 +281,7 @@
             Log.d(TAG, "File not found on backup: ");
             fnf.printStackTrace();
         } catch (RemoteException e) {
-            Log.d(TAG, "Remote target " + name + " threw during backup:");
+            Log.d(TAG, "Remote target " + packageName + " threw during backup:");
             e.printStackTrace();
         } catch (Exception e) {
             Log.w(TAG, "Final exception guard in backup: ");
@@ -289,37 +290,45 @@
         synchronized (mQueueLock) {
             mBackupQueue.remove(0);
         }
-        mContext.unbindService(mBackupHandler);
+
+        if (request != null) {
+            try {
+                mActivityManager.unbindBackupAgent(request.appInfo);
+            } catch (RemoteException e) {
+                // can't happen
+            }
+        }
 
         // start the next one
-        startOneService();
+        startOneAgent();
     }
 
-    // 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.
+    // Add the backup agents in the given package to our set of known backup participants.
+    // If 'packageName' is null, adds all backup agents in the whole system.
     void addPackageParticipantsLocked(String packageName) {
-        List<ResolveInfo> services = mPackageManager.queryIntentServices(
-                new Intent(BackupService.SERVICE_ACTION), 0);
-        addPackageParticipantsLockedInner(packageName, services);
+        // Look for apps that define the android:backupAgent attribute
+        List<ApplicationInfo> targetApps = allAgentApps();
+        addPackageParticipantsLockedInner(packageName, targetApps);
     }
 
-    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);
+    private void addPackageParticipantsLockedInner(String packageName,
+            List<ApplicationInfo> targetApps) {
+        if (DEBUG) {
+            Log.v(TAG, "Adding " + targetApps.size() + " backup participants:");
+            for (ApplicationInfo a : targetApps) {
+                Log.v(TAG, "    " + a + " agent=" + a.backupAgentName);
+            }
+        }
+
+        for (ApplicationInfo app : targetApps) {
+            if (packageName == null || app.packageName.equals(packageName)) {
+                int uid = app.uid;
+                HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
                 if (set == null) {
-                    set = new HashSet<ServiceInfo>();
+                    set = new HashSet<ApplicationInfo>();
                     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);
+                set.add(app);
             }
         }
     }
@@ -327,19 +336,30 @@
     // 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);
+        List<ApplicationInfo> allApps = null;
+        if (packageName != null) {
+            allApps = new ArrayList<ApplicationInfo>();
+            try {
+                ApplicationInfo app = mPackageManager.getApplicationInfo(packageName, 0);
+                allApps.add(app);
+            } catch (Exception e) {
+                // just skip it
+            }
+        } else {
+            // all apps with agents
+            allApps = allAgentApps();
+        }
+        removePackageParticipantsLockedInner(packageName, allApps);
     }
 
     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);
+            List<ApplicationInfo> agents) {
+        for (ApplicationInfo app : agents) {
+            if (packageName == null || app.packageName.equals(packageName)) {
+                int uid = app.uid;
+                HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
                 if (set != null) {
-                    set.remove(ri.serviceInfo);
+                    set.remove(app);
                     if (set.size() == 0) {
                         mBackupParticipants.put(uid, null);
                     }
@@ -348,6 +368,21 @@
         }
     }
 
+    // Returns the set of all applications that define an android:backupAgent attribute
+    private List<ApplicationInfo> allAgentApps() {
+        List<ApplicationInfo> allApps = mPackageManager.getInstalledApplications(0);
+        int N = allApps.size();
+        if (N > 0) {
+            for (int a = N-1; a >= 0; a--) {
+                ApplicationInfo app = allApps.get(a);
+                if (app.backupAgentName == null) {
+                    allApps.remove(a);
+                }
+            }
+        }
+        return allApps;
+    }
+    
     // 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) {
@@ -357,10 +392,9 @@
         }
 
         // brute force but small code size
-        List<ResolveInfo> services = mPackageManager.queryIntentServices(
-                new Intent(BackupService.SERVICE_ACTION), 0);
-        removePackageParticipantsLockedInner(packageName, services);
-        addPackageParticipantsLockedInner(packageName, services);
+        List<ApplicationInfo> allApps = allAgentApps();
+        removePackageParticipantsLockedInner(packageName, allApps);
+        addPackageParticipantsLockedInner(packageName, allApps);
     }
 
     // ----- IBackupManager binder interface -----
@@ -372,24 +406,29 @@
 
         Log.d(TAG, "dataChanged packageName=" + packageName);
         
-        HashSet<ServiceInfo> targets = mBackupParticipants.get(Binder.getCallingUid());
-        Log.d(TAG, "targets=" + targets);
+        HashSet<ApplicationInfo> targets = mBackupParticipants.get(Binder.getCallingUid());
         if (targets != null) {
             synchronized (mQueueLock) {
                 // Note that this client has made data changes that need to be backed up
-                for (ServiceInfo service : targets) {
+                for (ApplicationInfo app : targets) {
                     // validate the caller-supplied package name against the known set of
                     // packages associated with this uid
-                    if (service.packageName.equals(packageName)) {
+                    if (app.packageName.equals(packageName)) {
                         // Add the caller to the set of pending backups.  If there is
                         // one already there, then overwrite it, but no harm done.
-                        mPendingBackups.put(new ComponentName(service.packageName, service.name),
-                                new BackupRequest(service, true));
+                        BackupRequest req = new BackupRequest(app, false);
+                        mPendingBackups.put(app, req);
                         // !!! TODO: write to the pending-backup journal file in case of crash
                     }
                 }
 
-                Log.d(TAG, "Scheduling backup for " + mPendingBackups.size() + " participants");
+                if (DEBUG) {
+                    int numKeys = mPendingBackups.size();
+                    Log.d(TAG, "Scheduling backup for " + numKeys + " participants:");
+                    for (BackupRequest b : mPendingBackups.values()) {
+                        Log.d(TAG, "    + " + b + " agent=" + b.appInfo.backupAgentName);
+                    }
+                }
                 // Schedule a backup pass in a few minutes.  As backup-eligible data
                 // keeps changing, continue to defer the backup pass until things
                 // settle down, to avoid extra overhead.
@@ -402,22 +441,35 @@
     // that uid or package itself.
     public void scheduleFullBackup(String packageName) throws RemoteException {
         // !!! TODO: protect with a signature-or-system permission?
-        HashSet<ServiceInfo> targets = new HashSet<ServiceInfo>();
         synchronized (mQueueLock) {
             int numKeys = mBackupParticipants.size();
             for (int index = 0; index < numKeys; index++) {
                 int uid = mBackupParticipants.keyAt(index);
-                HashSet<ServiceInfo> servicesAtUid = mBackupParticipants.get(uid);
-                for (ServiceInfo service: servicesAtUid) {
-                    if (service.packageName.equals(packageName)) {
-                        mPendingBackups.put(new ComponentName(service.packageName, service.name),
-                                new BackupRequest(service, true));
+                HashSet<ApplicationInfo> servicesAtUid = mBackupParticipants.get(uid);
+                for (ApplicationInfo app: servicesAtUid) {
+                    if (app.packageName.equals(packageName)) {
+                        mPendingBackups.put(app, new BackupRequest(app, true));
                     }
                 }
             }
         }
     }
 
+    // Callback: a requested backup agent has been instantiated
+    public void agentConnected(String packageName, IBinder agentBinder) {
+        Log.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
+        IBackupAgent bs = IBackupAgent.Stub.asInterface(agentBinder);
+        processOneBackup(packageName, bs);
+    }
+
+    // Callback: a backup agent has failed to come up, or has unexpectedly quit.
+    // If the agent failed to come up in the first place, the agentBinder argument
+    // will be null.
+    public void agentDisconnected(String packageName) {
+        // TODO: handle backup being interrupted
+    }
+    
+
     
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -428,12 +480,18 @@
                 int uid = mBackupParticipants.keyAt(i);
                 pw.print("  uid: ");
                 pw.println(uid);
-                HashSet<ServiceInfo> services = mBackupParticipants.valueAt(i);
-                for (ServiceInfo s: services) {
+                HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
+                for (ApplicationInfo app: participants) {
                     pw.print("    ");
-                    pw.println(s.toString());
+                    pw.println(app.toString());
                 }
             }
+            pw.println("Pending:");
+            Iterator<BackupRequest> br = mPendingBackups.values().iterator();
+            while (br.hasNext()) {
+                pw.print("    ");
+                pw.println(br);
+            }
         }
     }
 }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index d1c40b4..3b26cb7 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -42,6 +42,7 @@
 import android.app.Instrumentation;
 import android.app.PendingIntent;
 import android.app.ResultInfo;
+import android.backup.IBackupManager;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -131,6 +132,7 @@
     static final boolean DEBUG_PROCESSES = localLOGV || false;
     static final boolean DEBUG_USER_LEAVING = localLOGV || false;
     static final boolean DEBUG_RESULTS = localLOGV || false;
+    static final boolean DEBUG_BACKUP = localLOGV || true;
     static final boolean VALIDATE_TOKENS = false;
     static final boolean SHOW_ACTIVITY_START_TIME = true;
     
@@ -633,6 +635,12 @@
             = new ArrayList<ServiceRecord>();
 
     /**
+     * Backup/restore process management
+     */
+    String mBackupAppName = null;
+    BackupRecord mBackupTarget = null;
+
+    /**
      * List of PendingThumbnailsRecord objects of clients who are still
      * waiting to receive all of the thumbnails for a task.
      */
@@ -4669,6 +4677,16 @@
                 mPendingBroadcast = null;
                 scheduleBroadcastsLocked();
             }
+            if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
+                Log.w(TAG, "Unattached app died before backup, skipping");
+                try {
+                    IBackupManager bm = IBackupManager.Stub.asInterface(
+                            ServiceManager.getService(Context.BACKUP_SERVICE));
+                    bm.agentDisconnected(app.info.packageName);
+                } catch (RemoteException e) {
+                    // Can't happen; the backup manager is local
+                }
+            }
         } else {
             Log.w(TAG, "Spurious process start timeout - pid not known for " + app);
         }
@@ -4757,11 +4775,17 @@
                     mWaitForDebugger = mOrigWaitForDebugger;
                 }
             }
+            // If the app is being launched for restore or full backup, set it up specially
+            boolean isRestrictedBackupMode = false;
+            if (mBackupTarget != null && mBackupAppName.equals(processName)) {
+                isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)
+                        || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
+            }
             thread.bindApplication(processName, app.instrumentationInfo != null
                     ? app.instrumentationInfo : app.info, providers,
                     app.instrumentationClass, app.instrumentationProfileFile,
                     app.instrumentationArguments, app.instrumentationWatcher, testMode, 
-                    mConfiguration, getCommonServicesLocked());
+                    isRestrictedBackupMode, mConfiguration, getCommonServicesLocked());
             updateLRUListLocked(app, false);
             app.lastRequestedGc = SystemClock.uptimeMillis();
         } catch (Exception e) {
@@ -4842,6 +4866,17 @@
             }
         }
 
+        // Check whether the next backup agent is in this process...
+        if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) {
+            if (DEBUG_BACKUP) Log.v(TAG, "New app is backup target, launching agent for " + app);
+            try {
+                thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode);
+            } catch (Exception e) {
+                Log.w(TAG, "Exception scheduling backup agent creation: ");
+                e.printStackTrace();
+            }
+        }
+
         if (badApp) {
             // todo: Also need to kill application to deal with all
             // kinds of exceptions.
@@ -9118,6 +9153,18 @@
             app.receivers.clear();
         }
         
+        // If the app is undergoing backup, tell the backup manager about it
+        if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
+            if (DEBUG_BACKUP) Log.d(TAG, "App " + mBackupTarget.appInfo + " died during backup");
+            try {
+                IBackupManager bm = IBackupManager.Stub.asInterface(
+                        ServiceManager.getService(Context.BACKUP_SERVICE));
+                bm.agentDisconnected(app.info.packageName);
+            } catch (RemoteException e) {
+                // can't happen; backup manager is local
+            }
+        }
+
         // If the caller is restarting this app, then leave it in its
         // current lists and let the caller take care of it.
         if (restarting) {
@@ -10234,6 +10281,105 @@
     }
     
     // =========================================================
+    // BACKUP AND RESTORE
+    // =========================================================
+    
+    // Cause the target app to be launched if necessary and its backup agent
+    // instantiated.  The backup agent will invoke backupAgentCreated() on the
+    // activity manager to announce its creation.
+    public boolean bindBackupAgent(ApplicationInfo app, int backupMode) {
+        if (DEBUG_BACKUP) Log.v(TAG, "startBackupAgent: app=" + app + " mode=" + backupMode);
+        enforceCallingPermission("android.permission.BACKUP", "startBackupAgent");
+
+        synchronized(this) {
+            // !!! TODO: currently no check here that we're already bound
+            BatteryStatsImpl.Uid.Pkg.Serv ss = null;
+            BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+            synchronized (stats) {
+                ss = stats.getServiceStatsLocked(app.uid, app.packageName, app.name);
+            }
+
+            BackupRecord r = new BackupRecord(ss, app, backupMode);
+            ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName);
+            // startProcessLocked() returns existing proc's record if it's already running
+            ProcessRecord proc = startProcessLocked(app.processName, app,
+                    false, 0, "backup", hostingName);
+            if (proc == null) {
+                Log.e(TAG, "Unable to start backup agent process " + r);
+                return false;
+            }
+
+            r.app = proc;
+            mBackupTarget = r;
+            mBackupAppName = app.packageName;
+
+            // If the process is already attached, schedule the creation of the backup agent now.
+            // If it is not yet live, this will be done when it attaches to the framework.
+            if (proc.thread != null) {
+                if (DEBUG_BACKUP) Log.v(TAG, "Agent proc already running: " + proc);
+                try {
+                    proc.thread.scheduleCreateBackupAgent(app, backupMode);
+                } catch (RemoteException e) {
+                    // !!! TODO: notify the backup manager that we crashed, or rely on
+                    // death notices, or...?
+                }
+            } else {
+                if (DEBUG_BACKUP) Log.v(TAG, "Agent proc not running, waiting for attach");
+            }
+            // Invariants: at this point, the target app process exists and the application
+            // is either already running or in the process of coming up.  mBackupTarget and
+            // mBackupAppName describe the app, so that when it binds back to the AM we
+            // know that it's scheduled for a backup-agent operation.
+        }
+        
+        return true;
+    }
+
+    // A backup agent has just come up                    
+    public void backupAgentCreated(String agentPackageName, IBinder agent) {
+        if (DEBUG_BACKUP) Log.v(TAG, "backupAgentCreated: " + agentPackageName
+                + " = " + agent);
+
+        synchronized(this) {
+            if (!agentPackageName.equals(mBackupAppName)) {
+                Log.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!");
+                return;
+            }
+
+            try {
+                IBackupManager bm = IBackupManager.Stub.asInterface(
+                        ServiceManager.getService(Context.BACKUP_SERVICE));
+                bm.agentConnected(agentPackageName, agent);
+            } catch (RemoteException e) {
+                // can't happen; the backup manager service is local
+            } catch (Exception e) {
+                Log.w(TAG, "Exception trying to deliver BackupAgent binding: ");
+                e.printStackTrace();
+            }
+        }
+    }
+
+    // done with this agent
+    public void unbindBackupAgent(ApplicationInfo appInfo) {
+        if (DEBUG_BACKUP) Log.v(TAG, "unbindBackupAgent: " + appInfo);
+
+        synchronized(this) {
+            if (!mBackupAppName.equals(appInfo.packageName)) {
+                Log.e(TAG, "Unbind of " + appInfo + " but is not the current backup target");
+                return;
+            }
+
+            try {
+                mBackupTarget.app.thread.scheduleDestroyBackupAgent(appInfo);
+            } catch (Exception e) {
+                Log.e(TAG, "Exception when unbinding backup agent:");
+                e.printStackTrace();
+            }
+            mBackupTarget = null;
+            mBackupAppName = null;
+        }
+    }
+    // =========================================================
     // BROADCASTS
     // =========================================================
 
diff --git a/services/java/com/android/server/am/BackupRecord.java b/services/java/com/android/server/am/BackupRecord.java
new file mode 100644
index 0000000..5ac8e0d
--- /dev/null
+++ b/services/java/com/android/server/am/BackupRecord.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import com.android.internal.os.BatteryStatsImpl;
+
+import android.content.pm.ApplicationInfo;
+
+/** @hide */
+class BackupRecord {
+    // backup/restore modes
+    public static final int BACKUP_NORMAL = 0;
+    public static final int BACKUP_FULL = 1;
+    public static final int RESTORE = 2;
+    
+    final BatteryStatsImpl.Uid.Pkg.Serv stats;
+    String stringName;                     // cached toString() output
+    final ApplicationInfo appInfo;         // information about BackupAgent's app
+    final int backupMode;                  // full backup / incremental / restore
+    ProcessRecord app;                     // where this agent is running or null
+
+    // ----- Implementation -----
+
+    BackupRecord(BatteryStatsImpl.Uid.Pkg.Serv _agentStats, ApplicationInfo _appInfo,
+            int _backupMode) {
+        stats = _agentStats;
+        appInfo = _appInfo;
+        backupMode = _backupMode;
+    }
+
+    public String toString() {
+        if (stringName != null) {
+            return stringName;
+        }
+        StringBuilder sb = new StringBuilder(128);
+        sb.append("BackupRecord{")
+            .append(Integer.toHexString(System.identityHashCode(this)))
+            .append(' ').append(appInfo.packageName)
+            .append(' ').append(appInfo.name)
+            .append(' ').append(appInfo.backupAgentName).append('}');
+        return stringName = sb.toString();
+    }
+}
\ No newline at end of file
diff --git a/tests/backup/AndroidManifest.xml b/tests/backup/AndroidManifest.xml
index eaeb5b7..3778742 100644
--- a/tests/backup/AndroidManifest.xml
+++ b/tests/backup/AndroidManifest.xml
@@ -1,6 +1,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.backuptest">
-    <application>
+    <application android:backupAgent="BackupTestAgent">
         <activity android:name="BackupTestActivity" android:label="_BackupTest">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -8,10 +8,5 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <service android:name="BackupTestService">
-           <intent-filter>
-               <action android:name="android.backup.BackupService.SERVICE" />
-           </intent-filter>
-        </service>
     </application>
 </manifest>
diff --git a/tests/backup/src/com/android/backuptest/BackupTestService.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
similarity index 89%
rename from tests/backup/src/com/android/backuptest/BackupTestService.java
rename to tests/backup/src/com/android/backuptest/BackupTestAgent.java
index 00eb86e..11e520e 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestService.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java
@@ -16,15 +16,15 @@
 
 package com.android.backuptest;
 
-import android.backup.BackupService;
+import android.app.BackupAgent;
 import android.backup.BackupDataOutput;
 import android.backup.FileBackupHelper;
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
-public class BackupTestService extends BackupService
+public class BackupTestAgent extends BackupAgent
 {
-    static final String TAG = "BackupTestService";
+    static final String TAG = "BackupTestAgent";
 
     @Override
     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,