summaryrefslogtreecommitdiffstats
path: root/core/java/android/app/backup/BackupAgent.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/app/backup/BackupAgent.java')
-rw-r--r--core/java/android/app/backup/BackupAgent.java251
1 files changed, 251 insertions, 0 deletions
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
new file mode 100644
index 0000000..5245da5
--- /dev/null
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -0,0 +1,251 @@
+/*
+ * 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.app.backup;
+
+import android.app.IBackupAgent;
+import android.app.backup.IBackupManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Provides the central interface between an
+ * application and Android's data backup infrastructure. An application that wishes
+ * to participate in the backup and restore mechanism will declare a subclass of
+ * {@link android.app.backup.BackupAgent}, implement the
+ * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()}
+ * and {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} methods,
+ * and provide the name of its backup agent class in its {@code AndroidManifest.xml} file via
+ * the <code><a
+ * href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a></code>
+ * tag's {@code android:backupAgent} attribute.
+ * <h3>Basic Operation</h3>
+ * <p>
+ * When the application makes changes to data that it wishes to keep backed up,
+ * it should call the
+ * {@link android.app.backup.BackupManager#dataChanged() BackupManager.dataChanged()} method.
+ * This notifies the Android Backup Manager that the application needs an opportunity
+ * to update its backup image. The Backup Manager, in turn, schedules a
+ * backup pass to be performed at an opportune time.
+ * <p>
+ * Restore operations are typically performed only when applications are first
+ * installed on a device. At that time, the operating system checks to see whether
+ * there is a previously-saved data set available for the application being installed, and if so,
+ * begins an immediate restore pass to deliver the backup data as part of the installation
+ * process.
+ * <p>
+ * When a backup or restore pass is run, the application's process is launched
+ * (if not already running), the manifest-declared backup agent class (in the {@code
+ * android:backupAgent} attribute) is instantiated within
+ * that process, and the agent's {@link #onCreate()} method is invoked. This prepares the
+ * agent instance to run the actual backup or restore logic. At this point the
+ * agent's
+ * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()} or
+ * {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} method will be
+ * invoked as appropriate for the operation being performed.
+ * <p>
+ * A backup data set consists of one or more "entities," flattened binary data
+ * records that are each identified with a key string unique within the data set. Adding a
+ * record to the active data set or updating an existing record is done by simply
+ * writing new entity data under the desired key. Deleting an entity from the data set
+ * is done by writing an entity under that key with header specifying a negative data
+ * size, and no actual entity data.
+ * <p>
+ * <b>Helper Classes</b>
+ * <p>
+ * An extensible agent based on convenient helper classes is available in
+ * {@link android.app.backup.BackupAgentHelper}. That class is particularly
+ * suited to handling of simple file or {@link android.content.SharedPreferences}
+ * backup and restore.
+ *
+ * @see android.app.backup.BackupManager
+ * @see android.app.backup.BackupAgentHelper
+ * @see android.app.backup.BackupDataInput
+ * @see android.app.backup.BackupDataOutput
+ */
+public abstract class BackupAgent extends ContextWrapper {
+ private static final String TAG = "BackupAgent";
+ private static final boolean DEBUG = false;
+
+ public BackupAgent() {
+ super(null);
+ }
+
+ /**
+ * Provided as a convenience for agent implementations that need an opportunity
+ * to do one-time initialization before the actual backup or restore operation
+ * is begun.
+ * <p>
+ * Agents do not need to override this method.
+ */
+ public void onCreate() {
+ }
+
+ /**
+ * Provided as a convenience for agent implementations that need to do some
+ * sort of shutdown process after backup or restore is completed.
+ * <p>
+ * Agents do not need to override this method.
+ */
+ public void onDestroy() {
+ }
+
+ /**
+ * 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 <code>oldState</code> file
+ * descriptor. If <code>oldState</code> is <code>null</code>, 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 file descriptor wrapped in
+ * <code>newState</code>.
+ * <p>
+ * Each entity written to the {@link android.app.backup.BackupDataOutput}
+ * <code>data</code> stream will be transmitted
+ * over the current backup transport and stored in the remote data set under
+ * the key supplied as part of the entity. Writing an entity with a negative
+ * data size instructs the transport to delete whatever entity currently exists
+ * under that key from the remote data set.
+ *
+ * @param oldState An open, read-only ParcelFileDescriptor pointing to the
+ * last backup state provided by the application. May be
+ * <code>null</code>, in which case no prior state is being
+ * provided and the application should perform a full backup.
+ * @param data A structured wrapper around 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 newState An open, read/write ParcelFileDescriptor pointing to an
+ * empty file. The application should record the final backup
+ * state here after writing the requested data to the <code>data</code>
+ * output stream.
+ */
+ public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) throws IOException;
+
+ /**
+ * The application is being restored from backup and should replace any
+ * existing data with the contents of the backup. The backup data is
+ * provided through the <code>data</code> parameter. Once
+ * the restore is finished, the application should write a representation of
+ * the final state to the <code>newState</code> file descriptor.
+ * <p>
+ * The application is responsible for properly erasing its old data and
+ * replacing it with the data supplied to this method. No "clear user data"
+ * operation will be performed automatically by the operating system. The
+ * exception to this is in the case of a failed restore attempt: if
+ * onRestore() throws an exception, the OS will assume that the
+ * application's data may now be in an incoherent state, and will clear it
+ * before proceeding.
+ *
+ * @param data A structured wrapper around an open, read-only
+ * file descriptor pointing to a full snapshot of the
+ * application's data. The application should consume every
+ * entity represented in this data stream.
+ * @param appVersionCode The
+ * {@link android.R.styleable#AndroidManifest_versionCode android:versionCode}
+ * value of the application that backed up this particular data set. This
+ * makes it possible for an application's agent to distinguish among any
+ * possible older data versions when asked to perform the restore
+ * operation.
+ * @param newState An open, read/write ParcelFileDescriptor pointing to an
+ * empty file. The application should record the final backup
+ * state here after restoring its data from the <code>data</code> stream.
+ */
+ public abstract void onRestore(BackupDataInput data, int appVersionCode,
+ ParcelFileDescriptor newState)
+ throws IOException;
+
+
+ // ----- Core implementation -----
+
+ /** @hide */
+ public final 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 IBackupAgent.Stub {
+ private static final String TAG = "BackupServiceBinder";
+
+ public void doBackup(ParcelFileDescriptor oldState,
+ ParcelFileDescriptor data,
+ ParcelFileDescriptor newState,
+ int token, IBackupManager callbackBinder) throws RemoteException {
+ // Ensure that we're running with the app's normal permission level
+ long ident = Binder.clearCallingIdentity();
+
+ if (DEBUG) Log.v(TAG, "doBackup() invoked");
+ BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
+ try {
+ BackupAgent.this.onBackup(oldState, output, newState);
+ } catch (IOException ex) {
+ Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw new RuntimeException(ex);
+ } catch (RuntimeException ex) {
+ Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw ex;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ try {
+ callbackBinder.opComplete(token);
+ } catch (RemoteException e) {
+ // we'll time out anyway, so we're safe
+ }
+ }
+ }
+
+ public void doRestore(ParcelFileDescriptor data, int appVersionCode,
+ ParcelFileDescriptor newState,
+ int token, IBackupManager callbackBinder) throws RemoteException {
+ // Ensure that we're running with the app's normal permission level
+ long ident = Binder.clearCallingIdentity();
+
+ if (DEBUG) Log.v(TAG, "doRestore() invoked");
+ BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
+ try {
+ BackupAgent.this.onRestore(input, appVersionCode, newState);
+ } catch (IOException ex) {
+ Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw new RuntimeException(ex);
+ } catch (RuntimeException ex) {
+ Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+ throw ex;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ try {
+ callbackBinder.opComplete(token);
+ } catch (RemoteException e) {
+ // we'll time out anyway, so we're safe
+ }
+ }
+ }
+ }
+}