am 487529a: First baby steps towards settings backup
Merge commit '487529a70cd1479ae8d6bbfb356be7e72542c185'
* commit '487529a70cd1479ae8d6bbfb356be7e72542c185':
First baby steps towards settings backup
diff --git a/Android.mk b/Android.mk
index 88f023f..20c93e6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -70,6 +70,8 @@
core/java/android/app/ITransientNotification.aidl \
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 \
@@ -105,6 +107,7 @@
core/java/com/android/internal/app/IUsageStats.aidl \
core/java/com/android/internal/appwidget/IAppWidgetService.aidl \
core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
+ core/java/com/android/internal/backup/IBackupTransport.aidl \
core/java/com/android/internal/os/IResultReceiver.aidl \
core/java/com/android/internal/view/IInputContext.aidl \
core/java/com/android/internal/view/IInputContextCallback.aidl \
diff --git a/api/current.xml b/api/current.xml
index 1f73152a..9ad4bf4 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -23953,6 +23953,82 @@
</field>
</class>
</package>
+<package name="android.backup"
+>
+<class name="BackupService"
+ extends="android.app.Service"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="BackupService"
+ type="android.backup.BackupService"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onBackup"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="oldStateFd" type="int">
+</parameter>
+<parameter name="dataFd" type="int">
+</parameter>
+<parameter name="newStateFd" type="int">
+</parameter>
+</method>
+<method name="onBind"
+ return="android.os.IBinder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onRestore"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dataFd" type="int">
+</parameter>
+<parameter name="newStateFd" type="int">
+</parameter>
+</method>
+<field name="SERVICE_ACTION"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.service.action.BACKUP""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+</package>
<package name="android.content"
>
<class name="ActivityNotFoundException"
@@ -27117,6 +27193,17 @@
visibility="public"
>
</field>
+<field name="BACKUP_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""backup""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="BIND_AUTO_CREATE"
type="int"
transient="false"
diff --git a/core/java/android/backup/BackupService.java b/core/java/android/backup/BackupService.java
new file mode 100644
index 0000000..5cfa4f2
--- /dev/null
+++ b/core/java/android/backup/BackupService.java
@@ -0,0 +1,106 @@
+/*
+ * 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 android.backup;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Service;
+import android.backup.IBackupService;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * This is the central interface between an application and Android's
+ * settings backup mechanism.
+ *
+ * <p><em>Not hidden but API subject to change and should not be published</em>
+ */
+
+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.service.action.BACKUP";
+
+ /**
+ * The application is being asked to write any data changed since the
+ * last time it performed a backup operation. The state data recorded
+ * during the last backup pass is provided in the oldStateFd file descriptor.
+ * If oldStateFd is negative, no old state is available and the application
+ * should perform a full backup. In both cases, a representation of the
+ * final backup state after this pass should be written to the file pointed
+ * to by the newStateFd file descriptor.
+ *
+ * @param oldStateFd An open, read-only file descriptor pointing to the last
+ * backup state provided by the application. May be negative,
+ * in which case no prior state is being provided and the
+ * application should perform a full backup.
+ * @param dataFd An open, read/write file descriptor pointing to the backup data
+ * destination. Typically the application will use backup helper
+ * classes to write to this file.
+ * @param newStateFd An open, read/write file descriptor pointing to an empty
+ * file. The application should record the final backup state
+ * here after writing the requested data to dataFd.
+ */
+ public abstract void onBackup(int oldStateFd, int dataFd, int newStateFd);
+
+ /**
+ * The application is being restored from backup, and should replace any
+ * existing data with the contents of the backup. The backup data is
+ * provided in the file pointed to by the dataFd file descriptor. Once
+ * the restore is finished, the application should write a representation
+ * of the final state to the newStateFd file descriptor,
+ *
+ * @param dataFd An open, read-only file descriptor pointing to a full snapshot
+ * of the application's data.
+ * @param newStateFd An open, read/write file descriptor pointing to an empty
+ * file. The application should record the final backup state
+ * here after restoring its data from dataFd.
+ */
+ public abstract void onRestore(int dataFd, int newStateFd);
+
+
+ // ----- Core implementation -----
+
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ private final IBinder mBinder = new BackupServiceBinder().asBinder();
+
+ // ----- IBackupService binder interface -----
+ private class BackupServiceBinder extends IBackupService.Stub {
+ public void doBackup(int oldStateFd, int dataFd, int newStateFd)
+ throws RemoteException {
+ // !!! TODO - real implementation; for now just invoke the callbacks directly
+ Log.v("BackupServiceBinder", "doBackup() invoked");
+ BackupService.this.onBackup(oldStateFd, dataFd, newStateFd);
+ }
+
+ public void doRestore(int dataFd, int newStateFd) throws RemoteException {
+ // !!! TODO - real implementation; for now just invoke the callbacks directly
+ Log.v("BackupServiceBinder", "doRestore() invoked");
+ BackupService.this.onRestore(dataFd, newStateFd);
+ }
+ }
+}
diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl
new file mode 100644
index 0000000..40cebdd
--- /dev/null
+++ b/core/java/android/backup/IBackupManager.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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 android.backup;
+
+/**
+ * Direct interface to the Backup Manager Service that applications invoke on. The only
+ * operation currently needed is a simple notification that the app has made changes to
+ * data it wishes to back up, so the system should run a backup pass.
+ *
+ * {@hide pending API solidification}
+ */
+interface IBackupManager {
+ /**
+ * Tell the system service that the caller has made changes to its
+ * data, and therefore needs to undergo a backup pass.
+ */
+ oneway void dataChanged();
+}
diff --git a/core/java/android/backup/IBackupService.aidl b/core/java/android/backup/IBackupService.aidl
new file mode 100644
index 0000000..24544bd
--- /dev/null
+++ b/core/java/android/backup/IBackupService.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright 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 android.backup;
+
+/**
+ * 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.
+ *
+ * {@hide}
+ */
+interface IBackupService {
+ /**
+ * Request that the app perform an incremental backup.
+ *
+ * @param oldStateFd Read-only file containing the description blob of the
+ * app's data state as of the last backup operation's completion.
+ *
+ * @param dataFd Read-write file, empty when onBackup() is called, that
+ * is the data destination for this backup pass's incrementals.
+ *
+ * @param newStateFd Read-write file, empty when onBackup() is called,
+ * where the new state blob is to be recorded.
+ */
+ void doBackup(int oldStateFd, int dataFd, int newStateFd);
+
+ /**
+ * Restore an entire data snapshot to the application.
+ *
+ * @param dataFd Read-only file containing the full data snapshot of the
+ * app's backup. This is to be a <i>replacement</i> of the app's
+ * current data, not to be merged into it.
+ *
+ * @param newStateFd Read-write file, empty when onRestore() is called,
+ * that is to be written with the state description that holds after
+ * the restore has been completed.
+ */
+ void doRestore(int dataFd, int newStateFd);
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index e8daa6e..2aa3695 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1279,6 +1279,15 @@
public static final String APPWIDGET_SERVICE = "appwidget";
/**
+ * Use with {@link #getSystemService} to retrieve an
+ * {@blink android.backup.IBackupManager IBackupManager} for communicating
+ * with the backup mechanism.
+ *
+ * @see #getSystemService
+ */
+ public static final String BACKUP_SERVICE = "backup";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
new file mode 100644
index 0000000..d64a303
--- /dev/null
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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.internal.backup;
+
+/** {@hide} */
+interface IBackupTransport {
+}
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
new file mode 100644
index 0000000..de14c33
--- /dev/null
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -0,0 +1,191 @@
+/*
+ * 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;
+
+import android.backup.BackupService;
+import android.backup.IBackupService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import android.backup.IBackupManager;
+
+import java.lang.String;
+import java.util.HashSet;
+import java.util.List;
+
+class BackupManagerService extends IBackupManager.Stub {
+ private static final String TAG = "BackupManagerService";
+ private static final boolean DEBUG = true;
+
+ private static final long COLLECTION_INTERVAL = 3 * 60 * 1000;
+
+ private static final int MSG_RUN_BACKUP = 1;
+
+ private Context mContext;
+ private PackageManager mPackageManager;
+ 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>>();
+ // set of backup services that have pending changes
+ private HashSet<ServiceInfo> mPendingBackups = new HashSet<ServiceInfo>();
+ private final Object mQueueLock = new Object();
+
+
+ // ----- Handler that runs the actual backup process asynchronously -----
+
+ private class BackupHandler extends Handler implements ServiceConnection {
+ private volatile Object mBindSignaller = new Object();
+ private volatile boolean mBinding = false;
+ private IBackupService mTargetService = null;
+
+ public void handleMessage(Message msg) {
+
+ switch (msg.what) {
+ case MSG_RUN_BACKUP:
+ {
+ // snapshot the pending-backup set and work on that
+ HashSet<ServiceInfo> queue;
+ synchronized (mQueueLock) {
+ queue = mPendingBackups;
+ mPendingBackups = new HashSet<ServiceInfo>();
+ // !!! TODO: start a new backup-queue journal file too
+ }
+
+ // Walk the set of pending backups, setting up the relevant files and
+ // invoking the backup service in each participant
+ Intent backupIntent = new Intent(BackupService.SERVICE_ACTION);
+ for (ServiceInfo service : queue) {
+ mBinding = true;
+ mTargetService = null;
+
+ backupIntent.setClassName(service.packageName, service.name);
+ Log.d(TAG, "binding to " + backupIntent);
+ if (mContext.bindService(backupIntent, this, 0)) {
+ synchronized (mBindSignaller) {
+ while (mTargetService == null && mBinding == true) {
+ try {
+ mBindSignaller.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ if (mTargetService != null) {
+ try {
+ Log.d(TAG, "invoking doBackup() on " + backupIntent);
+ // !!! TODO: set up files
+ mTargetService.doBackup(-1, -1, -1);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Remote target " + backupIntent
+ + " threw during backup:");
+ e.printStackTrace();
+ }
+ mContext.unbindService(this);
+ }
+ } else {
+ Log.d(TAG, "Unable to bind to " + backupIntent);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mBindSignaller) {
+ mTargetService = IBackupService.Stub.asInterface(service);
+ mBinding = false;
+ mBindSignaller.notifyAll();
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mBindSignaller) {
+ mTargetService = null;
+ mBinding = false;
+ mBindSignaller.notifyAll();
+ }
+ }
+ }
+
+ public BackupManagerService(Context context) {
+ mContext = context;
+ mPackageManager = context.getPackageManager();
+
+ // Identify the backup participants
+ // !!! TODO: also watch package-install to keep this up to date
+ List<ResolveInfo> services = mPackageManager.queryIntentServices(
+ new Intent(BackupService.SERVICE_ACTION), 0);
+ if (DEBUG) {
+ Log.v(TAG, "Backup participants: " + services.size());
+ for (ResolveInfo ri : services) {
+ Log.v(TAG, " " + ri + " : " + ri.filter);
+ }
+ }
+
+ // Build our mapping of uid to backup client services
+ for (ResolveInfo ri : services) {
+ int uid = ri.serviceInfo.applicationInfo.uid;
+ HashSet<ServiceInfo> set = mBackupParticipants.get(uid);
+ if (set == null) {
+ set = new HashSet<ServiceInfo>();
+ mBackupParticipants.put(uid, set);
+ }
+ set.add(ri.serviceInfo);
+ }
+ }
+
+
+ // ----- IBackupManager binder interface -----
+
+ public void dataChanged() throws RemoteException {
+ // Record that we need a backup pass for the caller. Since multiple callers
+ // may share a uid, we need to note all candidates within that uid and schedule
+ // a backup pass for each of them.
+
+ HashSet<ServiceInfo> targets = mBackupParticipants.get(Binder.getCallingUid());
+ if (targets != null) {
+ synchronized (mQueueLock) {
+ // Note that this client has made data changes that need to be backed up
+ // !!! add them to the set of pending packages
+ for (ServiceInfo service : targets) {
+ if (mPendingBackups.add(service)) {
+ // !!! TODO: write to the pending-backup journal file in case of crash
+ }
+ }
+
+ // 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.
+ mBackupHandler.removeMessages(MSG_RUN_BACKUP);
+ mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL);
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index efca2cb..b19e2ee 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -310,6 +310,13 @@
}
try {
+ Log.i(TAG, "Starting Backup Service");
+ ServiceManager.addService(Context.BACKUP_SERVICE, new BackupManagerService(context));
+ } catch (Throwable e) {
+ Log.e(TAG, "Failure starting Backup Service", e);
+ }
+
+ try {
Log.i(TAG, "Starting AppWidget Service");
appWidget = new AppWidgetService(context);
ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget);