Home | History | Annotate | Download | only in backup
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.app.backup;
     18 
     19 import android.app.IBackupAgent;
     20 import android.app.backup.IBackupManager;
     21 import android.content.Context;
     22 import android.content.ContextWrapper;
     23 import android.os.Binder;
     24 import android.os.IBinder;
     25 import android.os.ParcelFileDescriptor;
     26 import android.os.RemoteException;
     27 import android.util.Log;
     28 
     29 import java.io.IOException;
     30 
     31 /**
     32  * Provides the central interface between an
     33  * application and Android's data backup infrastructure.  An application that wishes
     34  * to participate in the backup and restore mechanism will declare a subclass of
     35  * {@link android.app.backup.BackupAgent}, implement the
     36  * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()}
     37  * and {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} methods,
     38  * and provide the name of its backup agent class in its {@code AndroidManifest.xml} file via
     39  * the <code><a
     40  * href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a></code>
     41  * tag's {@code android:backupAgent} attribute.
     42  * <h3>Basic Operation</h3>
     43  * <p>
     44  * When the application makes changes to data that it wishes to keep backed up,
     45  * it should call the
     46  * {@link android.app.backup.BackupManager#dataChanged() BackupManager.dataChanged()} method.
     47  * This notifies the Android Backup Manager that the application needs an opportunity
     48  * to update its backup image.  The Backup Manager, in turn, schedules a
     49  * backup pass to be performed at an opportune time.
     50  * <p>
     51  * Restore operations are typically performed only when applications are first
     52  * installed on a device.  At that time, the operating system checks to see whether
     53  * there is a previously-saved data set available for the application being installed, and if so,
     54  * begins an immediate restore pass to deliver the backup data as part of the installation
     55  * process.
     56  * <p>
     57  * When a backup or restore pass is run, the application's process is launched
     58  * (if not already running), the manifest-declared backup agent class (in the {@code
     59  * android:backupAgent} attribute) is instantiated within
     60  * that process, and the agent's {@link #onCreate()} method is invoked.  This prepares the
     61  * agent instance to run the actual backup or restore logic.  At this point the
     62  * agent's
     63  * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()} or
     64  * {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} method will be
     65  * invoked as appropriate for the operation being performed.
     66  * <p>
     67  * A backup data set consists of one or more "entities," flattened binary data
     68  * records that are each identified with a key string unique within the data set.  Adding a
     69  * record to the active data set or updating an existing record is done by simply
     70  * writing new entity data under the desired key.  Deleting an entity from the data set
     71  * is done by writing an entity under that key with header specifying a negative data
     72  * size, and no actual entity data.
     73  * <p>
     74  * <b>Helper Classes</b>
     75  * <p>
     76  * An extensible agent based on convenient helper classes is available in
     77  * {@link android.app.backup.BackupAgentHelper}.  That class is particularly
     78  * suited to handling of simple file or {@link android.content.SharedPreferences}
     79  * backup and restore.
     80  *
     81  * @see android.app.backup.BackupManager
     82  * @see android.app.backup.BackupAgentHelper
     83  * @see android.app.backup.BackupDataInput
     84  * @see android.app.backup.BackupDataOutput
     85  */
     86 public abstract class BackupAgent extends ContextWrapper {
     87     private static final String TAG = "BackupAgent";
     88     private static final boolean DEBUG = false;
     89 
     90     public BackupAgent() {
     91         super(null);
     92     }
     93 
     94     /**
     95      * Provided as a convenience for agent implementations that need an opportunity
     96      * to do one-time initialization before the actual backup or restore operation
     97      * is begun.
     98      * <p>
     99      * Agents do not need to override this method.
    100      */
    101     public void onCreate() {
    102     }
    103 
    104     /**
    105      * Provided as a convenience for agent implementations that need to do some
    106      * sort of shutdown process after backup or restore is completed.
    107      * <p>
    108      * Agents do not need to override this method.
    109      */
    110     public void onDestroy() {
    111     }
    112 
    113     /**
    114      * The application is being asked to write any data changed since the last
    115      * time it performed a backup operation. The state data recorded during the
    116      * last backup pass is provided in the <code>oldState</code> file
    117      * descriptor. If <code>oldState</code> is <code>null</code>, no old state
    118      * is available and the application should perform a full backup. In both
    119      * cases, a representation of the final backup state after this pass should
    120      * be written to the file pointed to by the file descriptor wrapped in
    121      * <code>newState</code>.
    122      * <p>
    123      * Each entity written to the {@link android.app.backup.BackupDataOutput}
    124      * <code>data</code> stream will be transmitted
    125      * over the current backup transport and stored in the remote data set under
    126      * the key supplied as part of the entity.  Writing an entity with a negative
    127      * data size instructs the transport to delete whatever entity currently exists
    128      * under that key from the remote data set.
    129      *
    130      * @param oldState An open, read-only ParcelFileDescriptor pointing to the
    131      *            last backup state provided by the application. May be
    132      *            <code>null</code>, in which case no prior state is being
    133      *            provided and the application should perform a full backup.
    134      * @param data A structured wrapper around an open, read/write
    135      *            file descriptor pointing to the backup data destination.
    136      *            Typically the application will use backup helper classes to
    137      *            write to this file.
    138      * @param newState An open, read/write ParcelFileDescriptor pointing to an
    139      *            empty file. The application should record the final backup
    140      *            state here after writing the requested data to the <code>data</code>
    141      *            output stream.
    142      */
    143     public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
    144              ParcelFileDescriptor newState) throws IOException;
    145 
    146     /**
    147      * The application is being restored from backup and should replace any
    148      * existing data with the contents of the backup. The backup data is
    149      * provided through the <code>data</code> parameter. Once
    150      * the restore is finished, the application should write a representation of
    151      * the final state to the <code>newState</code> file descriptor.
    152      * <p>
    153      * The application is responsible for properly erasing its old data and
    154      * replacing it with the data supplied to this method. No "clear user data"
    155      * operation will be performed automatically by the operating system. The
    156      * exception to this is in the case of a failed restore attempt: if
    157      * onRestore() throws an exception, the OS will assume that the
    158      * application's data may now be in an incoherent state, and will clear it
    159      * before proceeding.
    160      *
    161      * @param data A structured wrapper around an open, read-only
    162      *            file descriptor pointing to a full snapshot of the
    163      *            application's data.  The application should consume every
    164      *            entity represented in this data stream.
    165      * @param appVersionCode The value of the <a
    166      * href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code
    167      *            android:versionCode}</a> manifest attribute,
    168      *            from the application that backed up this particular data set. This
    169      *            makes it possible for an application's agent to distinguish among any
    170      *            possible older data versions when asked to perform the restore
    171      *            operation.
    172      * @param newState An open, read/write ParcelFileDescriptor pointing to an
    173      *            empty file. The application should record the final backup
    174      *            state here after restoring its data from the <code>data</code> stream.
    175      */
    176     public abstract void onRestore(BackupDataInput data, int appVersionCode,
    177             ParcelFileDescriptor newState)
    178             throws IOException;
    179 
    180 
    181     // ----- Core implementation -----
    182 
    183     /** @hide */
    184     public final IBinder onBind() {
    185         return mBinder;
    186     }
    187 
    188     private final IBinder mBinder = new BackupServiceBinder().asBinder();
    189 
    190     /** @hide */
    191     public void attach(Context context) {
    192         attachBaseContext(context);
    193     }
    194 
    195     // ----- IBackupService binder interface -----
    196     private class BackupServiceBinder extends IBackupAgent.Stub {
    197         private static final String TAG = "BackupServiceBinder";
    198 
    199         public void doBackup(ParcelFileDescriptor oldState,
    200                 ParcelFileDescriptor data,
    201                 ParcelFileDescriptor newState,
    202                 int token, IBackupManager callbackBinder) throws RemoteException {
    203             // Ensure that we're running with the app's normal permission level
    204             long ident = Binder.clearCallingIdentity();
    205 
    206             if (DEBUG) Log.v(TAG, "doBackup() invoked");
    207             BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor());
    208             try {
    209                 BackupAgent.this.onBackup(oldState, output, newState);
    210             } catch (IOException ex) {
    211                 Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
    212                 throw new RuntimeException(ex);
    213             } catch (RuntimeException ex) {
    214                 Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
    215                 throw ex;
    216             } finally {
    217                 Binder.restoreCallingIdentity(ident);
    218                 try {
    219                     callbackBinder.opComplete(token);
    220                 } catch (RemoteException e) {
    221                     // we'll time out anyway, so we're safe
    222                 }
    223             }
    224         }
    225 
    226         public void doRestore(ParcelFileDescriptor data, int appVersionCode,
    227                 ParcelFileDescriptor newState,
    228                 int token, IBackupManager callbackBinder) throws RemoteException {
    229             // Ensure that we're running with the app's normal permission level
    230             long ident = Binder.clearCallingIdentity();
    231 
    232             if (DEBUG) Log.v(TAG, "doRestore() invoked");
    233             BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
    234             try {
    235                 BackupAgent.this.onRestore(input, appVersionCode, newState);
    236             } catch (IOException ex) {
    237                 Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
    238                 throw new RuntimeException(ex);
    239             } catch (RuntimeException ex) {
    240                 Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
    241                 throw ex;
    242             } finally {
    243                 Binder.restoreCallingIdentity(ident);
    244                 try {
    245                     callbackBinder.opComplete(token);
    246                 } catch (RemoteException e) {
    247                     // we'll time out anyway, so we're safe
    248                 }
    249             }
    250         }
    251     }
    252 }
    253