diff options
Diffstat (limited to 'core/java/android/app/backup/BackupAgent.java')
-rw-r--r-- | core/java/android/app/backup/BackupAgent.java | 251 |
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"><application></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 + } + } + } + } +} |