BackupAgent.java revision b83a283ac178ab0a72f1d811189d79b26097835e
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
17package android.app.backup;
18
19import android.app.IBackupAgent;
20import android.app.backup.IBackupManager;
21import android.content.Context;
22import android.content.ContextWrapper;
23import android.os.Binder;
24import android.os.IBinder;
25import android.os.ParcelFileDescriptor;
26import android.os.RemoteException;
27import android.util.Log;
28
29import 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 */
86public 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