| /* |
| * 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; |
| |
| /** |
| * {@link android.app.backup.BackupAgent} is 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)} |
| * and {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor)} methods, |
| * and provide the name of its agent class in the AndroidManifest.xml file via |
| * the <application> tag's android:backupAgent attribute. |
| * <p> |
| * <b>Basic Operation</b> |
| * <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, will then schedule a |
| * backup pass to be performed at an opportune time. |
| * <p> |
| * Restore operations are typically only performed 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, and if so, |
| * begins an immediate restore pass to deliver that data as part of the installation |
| * process. |
| * <p> |
| * When a backup or restore pass is run, the application's process will be launched |
| * (if not already running), the manifest-declared agent class instantiated within |
| * that process, and the agent's {@link #onCreate()} method 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, are 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 |
| } |
| } |
| } |
| } |
| } |