Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 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 | |
Christopher Tate | 4528186 | 2010-03-05 15:46:30 -0800 | [diff] [blame] | 17 | package android.app.backup; |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 18 | |
Christopher Tate | 181fafa | 2009-05-14 11:12:14 -0700 | [diff] [blame] | 19 | import android.app.IBackupAgent; |
Christopher Tate | 4528186 | 2010-03-05 15:46:30 -0800 | [diff] [blame] | 20 | import android.app.backup.IBackupManager; |
Christopher Tate | 181fafa | 2009-05-14 11:12:14 -0700 | [diff] [blame] | 21 | import android.content.Context; |
| 22 | import android.content.ContextWrapper; |
Christopher Tate | 1902492 | 2010-01-22 16:39:53 -0800 | [diff] [blame] | 23 | import android.os.Binder; |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 24 | import android.os.IBinder; |
Christopher Tate | 22b8787 | 2009-05-04 16:41:53 -0700 | [diff] [blame] | 25 | import android.os.ParcelFileDescriptor; |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 26 | import android.os.RemoteException; |
| 27 | import android.util.Log; |
| 28 | |
Joe Onorato | 83248c4 | 2009-06-17 17:55:20 -0700 | [diff] [blame] | 29 | import java.io.IOException; |
| 30 | |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 31 | /** |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 32 | * Provides the central interface between an |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 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 |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 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"><application></a></code> |
| 41 | * tag's {@code android:backupAgent} attribute. |
| 42 | * <h3>Basic Operation</h3> |
Kenny Root | 5a20ea1 | 2010-02-23 18:49:11 -0800 | [diff] [blame] | 43 | * <p> |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 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. |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 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 |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 49 | * backup pass to be performed at an opportune time. |
| 50 | * <p> |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 51 | * Restore operations are typically performed only when applications are first |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 52 | * installed on a device. At that time, the operating system checks to see whether |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 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 |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 55 | * process. |
| 56 | * <p> |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 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 |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 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> |
Scott Main | d17da43 | 2010-04-29 21:42:58 -0700 | [diff] [blame] | 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 |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 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 |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 85 | */ |
Christopher Tate | 181fafa | 2009-05-14 11:12:14 -0700 | [diff] [blame] | 86 | public abstract class BackupAgent extends ContextWrapper { |
Joe Onorato | 83248c4 | 2009-06-17 17:55:20 -0700 | [diff] [blame] | 87 | private static final String TAG = "BackupAgent"; |
Christopher Tate | 436344a | 2009-09-30 16:17:37 -0700 | [diff] [blame] | 88 | private static final boolean DEBUG = false; |
Joe Onorato | 83248c4 | 2009-06-17 17:55:20 -0700 | [diff] [blame] | 89 | |
Christopher Tate | 181fafa | 2009-05-14 11:12:14 -0700 | [diff] [blame] | 90 | public BackupAgent() { |
| 91 | super(null); |
| 92 | } |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 93 | |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 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 | */ |
Christopher Tate | 181fafa | 2009-05-14 11:12:14 -0700 | [diff] [blame] | 101 | public void onCreate() { |
| 102 | } |
| 103 | |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 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 | */ |
Christopher Tate | 181fafa | 2009-05-14 11:12:14 -0700 | [diff] [blame] | 110 | public void onDestroy() { |
| 111 | } |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 112 | |
| 113 | /** |
Kenny Root | 5a20ea1 | 2010-02-23 18:49:11 -0800 | [diff] [blame] | 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>. |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 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. |
Kenny Root | 5a20ea1 | 2010-02-23 18:49:11 -0800 | [diff] [blame] | 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 |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 135 | * file descriptor pointing to the backup data destination. |
Kenny Root | 5a20ea1 | 2010-02-23 18:49:11 -0800 | [diff] [blame] | 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 |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 140 | * state here after writing the requested data to the <code>data</code> |
| 141 | * output stream. |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 142 | */ |
Joe Onorato | 290bb01 | 2009-05-13 18:57:29 -0400 | [diff] [blame] | 143 | public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, |
Joe Onorato | 4ababd9 | 2009-06-25 18:29:18 -0400 | [diff] [blame] | 144 | ParcelFileDescriptor newState) throws IOException; |
Kenny Root | 5a20ea1 | 2010-02-23 18:49:11 -0800 | [diff] [blame] | 145 | |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 146 | /** |
Kenny Root | 5a20ea1 | 2010-02-23 18:49:11 -0800 | [diff] [blame] | 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 |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 149 | * provided through the <code>data</code> parameter. Once |
Kenny Root | 5a20ea1 | 2010-02-23 18:49:11 -0800 | [diff] [blame] | 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 |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 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. |
Scott Main | b83a283 | 2010-04-29 13:26:53 -0700 | [diff] [blame] | 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 |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 169 | * makes it possible for an application's agent to distinguish among any |
Kenny Root | 5a20ea1 | 2010-02-23 18:49:11 -0800 | [diff] [blame] | 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 |
Christopher Tate | 4e14a82 | 2010-04-08 12:54:23 -0700 | [diff] [blame] | 174 | * state here after restoring its data from the <code>data</code> stream. |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 175 | */ |
Christopher Tate | 5cbbf56 | 2009-06-22 16:44:51 -0700 | [diff] [blame] | 176 | public abstract void onRestore(BackupDataInput data, int appVersionCode, |
| 177 | ParcelFileDescriptor newState) |
Joe Onorato | 83248c4 | 2009-06-17 17:55:20 -0700 | [diff] [blame] | 178 | throws IOException; |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 179 | |
| 180 | |
| 181 | // ----- Core implementation ----- |
Christopher Tate | 44a2790 | 2010-01-27 17:15:49 -0800 | [diff] [blame] | 182 | |
| 183 | /** @hide */ |
| 184 | public final IBinder onBind() { |
Christopher Tate | 181fafa | 2009-05-14 11:12:14 -0700 | [diff] [blame] | 185 | return mBinder; |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | private final IBinder mBinder = new BackupServiceBinder().asBinder(); |
| 189 | |
Christopher Tate | 181fafa | 2009-05-14 11:12:14 -0700 | [diff] [blame] | 190 | /** @hide */ |
| 191 | public void attach(Context context) { |
| 192 | attachBaseContext(context); |
| 193 | } |
| 194 | |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 195 | // ----- IBackupService binder interface ----- |
Christopher Tate | 181fafa | 2009-05-14 11:12:14 -0700 | [diff] [blame] | 196 | private class BackupServiceBinder extends IBackupAgent.Stub { |
| 197 | private static final String TAG = "BackupServiceBinder"; |
| 198 | |
Christopher Tate | 22b8787 | 2009-05-04 16:41:53 -0700 | [diff] [blame] | 199 | public void doBackup(ParcelFileDescriptor oldState, |
| 200 | ParcelFileDescriptor data, |
Christopher Tate | 44a2790 | 2010-01-27 17:15:49 -0800 | [diff] [blame] | 201 | ParcelFileDescriptor newState, |
| 202 | int token, IBackupManager callbackBinder) throws RemoteException { |
Christopher Tate | 1902492 | 2010-01-22 16:39:53 -0800 | [diff] [blame] | 203 | // Ensure that we're running with the app's normal permission level |
Christopher Tate | 44a2790 | 2010-01-27 17:15:49 -0800 | [diff] [blame] | 204 | long ident = Binder.clearCallingIdentity(); |
Christopher Tate | 1902492 | 2010-01-22 16:39:53 -0800 | [diff] [blame] | 205 | |
Christopher Tate | 436344a | 2009-09-30 16:17:37 -0700 | [diff] [blame] | 206 | if (DEBUG) Log.v(TAG, "doBackup() invoked"); |
Joe Onorato | 83248c4 | 2009-06-17 17:55:20 -0700 | [diff] [blame] | 207 | BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor()); |
Joe Onorato | 290bb01 | 2009-05-13 18:57:29 -0400 | [diff] [blame] | 208 | try { |
Christopher Tate | 181fafa | 2009-05-14 11:12:14 -0700 | [diff] [blame] | 209 | BackupAgent.this.onBackup(oldState, output, newState); |
Joe Onorato | 4ababd9 | 2009-06-25 18:29:18 -0400 | [diff] [blame] | 210 | } catch (IOException ex) { |
| 211 | Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); |
| 212 | throw new RuntimeException(ex); |
Joe Onorato | 290bb01 | 2009-05-13 18:57:29 -0400 | [diff] [blame] | 213 | } catch (RuntimeException ex) { |
Joe Onorato | 83248c4 | 2009-06-17 17:55:20 -0700 | [diff] [blame] | 214 | Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); |
Joe Onorato | 290bb01 | 2009-05-13 18:57:29 -0400 | [diff] [blame] | 215 | throw ex; |
Christopher Tate | 1902492 | 2010-01-22 16:39:53 -0800 | [diff] [blame] | 216 | } finally { |
Christopher Tate | 44a2790 | 2010-01-27 17:15:49 -0800 | [diff] [blame] | 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 | } |
Joe Onorato | 290bb01 | 2009-05-13 18:57:29 -0400 | [diff] [blame] | 223 | } |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 224 | } |
| 225 | |
Christopher Tate | 5cbbf56 | 2009-06-22 16:44:51 -0700 | [diff] [blame] | 226 | public void doRestore(ParcelFileDescriptor data, int appVersionCode, |
Christopher Tate | 44a2790 | 2010-01-27 17:15:49 -0800 | [diff] [blame] | 227 | ParcelFileDescriptor newState, |
| 228 | int token, IBackupManager callbackBinder) throws RemoteException { |
Christopher Tate | 1902492 | 2010-01-22 16:39:53 -0800 | [diff] [blame] | 229 | // Ensure that we're running with the app's normal permission level |
Christopher Tate | 44a2790 | 2010-01-27 17:15:49 -0800 | [diff] [blame] | 230 | long ident = Binder.clearCallingIdentity(); |
Christopher Tate | 1902492 | 2010-01-22 16:39:53 -0800 | [diff] [blame] | 231 | |
Christopher Tate | 436344a | 2009-09-30 16:17:37 -0700 | [diff] [blame] | 232 | if (DEBUG) Log.v(TAG, "doRestore() invoked"); |
Joe Onorato | 83248c4 | 2009-06-17 17:55:20 -0700 | [diff] [blame] | 233 | BackupDataInput input = new BackupDataInput(data.getFileDescriptor()); |
| 234 | try { |
Christopher Tate | 5cbbf56 | 2009-06-22 16:44:51 -0700 | [diff] [blame] | 235 | BackupAgent.this.onRestore(input, appVersionCode, newState); |
Joe Onorato | 83248c4 | 2009-06-17 17:55:20 -0700 | [diff] [blame] | 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; |
Christopher Tate | 1902492 | 2010-01-22 16:39:53 -0800 | [diff] [blame] | 242 | } finally { |
Christopher Tate | 44a2790 | 2010-01-27 17:15:49 -0800 | [diff] [blame] | 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 | } |
Joe Onorato | 83248c4 | 2009-06-17 17:55:20 -0700 | [diff] [blame] | 249 | } |
Christopher Tate | 487529a | 2009-04-29 14:03:25 -0700 | [diff] [blame] | 250 | } |
| 251 | } |
| 252 | } |