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.annotation.SystemApi; 20 import android.content.Context; 21 import android.os.Handler; 22 import android.os.Message; 23 import android.os.RemoteException; 24 import android.os.ServiceManager; 25 import android.util.Log; 26 import android.util.Pair; 27 28 /** 29 * The interface through which an application interacts with the Android backup service to 30 * request backup and restore operations. 31 * Applications instantiate it using the constructor and issue calls through that instance. 32 * <p> 33 * When an application has made changes to data which should be backed up, a 34 * call to {@link #dataChanged()} will notify the backup service. The system 35 * will then schedule a backup operation to occur in the near future. Repeated 36 * calls to {@link #dataChanged()} have no further effect until the backup 37 * operation actually occurs. 38 * <p> 39 * A backup or restore operation for your application begins when the system launches the 40 * {@link android.app.backup.BackupAgent} subclass you've declared in your manifest. See the 41 * documentation for {@link android.app.backup.BackupAgent} for a detailed description 42 * of how the operation then proceeds. 43 * <p> 44 * Several attributes affecting the operation of the backup and restore mechanism 45 * can be set on the <code> 46 * <a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code> 47 * tag in your application's AndroidManifest.xml file. 48 * 49 * <div class="special reference"> 50 * <h3>Developer Guides</h3> 51 * <p>For more information about using BackupManager, read the 52 * <a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a> developer guide.</p></div> 53 * 54 * @attr ref android.R.styleable#AndroidManifestApplication_allowBackup 55 * @attr ref android.R.styleable#AndroidManifestApplication_backupAgent 56 * @attr ref android.R.styleable#AndroidManifestApplication_killAfterRestore 57 * @attr ref android.R.styleable#AndroidManifestApplication_restoreAnyVersion 58 */ 59 public class BackupManager { 60 private static final String TAG = "BackupManager"; 61 62 // BackupObserver status codes 63 /** 64 * Indicates that backup succeeded. 65 * 66 * @hide 67 */ 68 @SystemApi 69 public static final int SUCCESS = 0; 70 71 /** 72 * Indicates that backup is either not enabled at all or 73 * backup for the package was rejected by backup service 74 * or backup transport, 75 * 76 * @hide 77 */ 78 @SystemApi 79 public static final int ERROR_BACKUP_NOT_ALLOWED = -2001; 80 81 /** 82 * The requested app is not installed on the device. 83 * 84 * @hide 85 */ 86 @SystemApi 87 public static final int ERROR_PACKAGE_NOT_FOUND = -2002; 88 89 /** 90 * The transport for some reason was not in a good state and 91 * aborted the entire backup request. This is a transient 92 * failure and should not be retried immediately. 93 * 94 * @hide 95 */ 96 @SystemApi 97 public static final int ERROR_TRANSPORT_ABORTED = BackupTransport.TRANSPORT_ERROR; 98 99 /** 100 * Returned when the transport was unable to process the 101 * backup request for a given package, for example if the 102 * transport hit a transient network failure. The remaining 103 * packages provided to {@link #requestBackup(String[], BackupObserver)} 104 * will still be attempted. 105 * 106 * @hide 107 */ 108 @SystemApi 109 public static final int ERROR_TRANSPORT_PACKAGE_REJECTED = 110 BackupTransport.TRANSPORT_PACKAGE_REJECTED; 111 112 /** 113 * Returned when the transport reject the attempt to backup because 114 * backup data size exceeded current quota limit for this package. 115 * 116 * @hide 117 */ 118 @SystemApi 119 public static final int ERROR_TRANSPORT_QUOTA_EXCEEDED = 120 BackupTransport.TRANSPORT_QUOTA_EXCEEDED; 121 122 /** 123 * The {@link BackupAgent} for the requested package failed for some reason 124 * and didn't provide appropriate backup data. 125 * 126 * @hide 127 */ 128 @SystemApi 129 public static final int ERROR_AGENT_FAILURE = BackupTransport.AGENT_ERROR; 130 131 private Context mContext; 132 private static IBackupManager sService; 133 checkServiceBinder()134 private static void checkServiceBinder() { 135 if (sService == null) { 136 sService = IBackupManager.Stub.asInterface( 137 ServiceManager.getService(Context.BACKUP_SERVICE)); 138 } 139 } 140 141 /** 142 * Constructs a BackupManager object through which the application can 143 * communicate with the Android backup system. 144 * 145 * @param context The {@link android.content.Context} that was provided when 146 * one of your application's {@link android.app.Activity Activities} 147 * was created. 148 */ BackupManager(Context context)149 public BackupManager(Context context) { 150 mContext = context; 151 } 152 153 /** 154 * Notifies the Android backup system that your application wishes to back up 155 * new changes to its data. A backup operation using your application's 156 * {@link android.app.backup.BackupAgent} subclass will be scheduled when you 157 * call this method. 158 */ dataChanged()159 public void dataChanged() { 160 checkServiceBinder(); 161 if (sService != null) { 162 try { 163 sService.dataChanged(mContext.getPackageName()); 164 } catch (RemoteException e) { 165 Log.d(TAG, "dataChanged() couldn't connect"); 166 } 167 } 168 } 169 170 /** 171 * Convenience method for callers who need to indicate that some other package 172 * needs a backup pass. This can be useful in the case of groups of packages 173 * that share a uid. 174 * <p> 175 * This method requires that the application hold the "android.permission.BACKUP" 176 * permission if the package named in the argument does not run under the same uid 177 * as the caller. 178 * 179 * @param packageName The package name identifying the application to back up. 180 */ dataChanged(String packageName)181 public static void dataChanged(String packageName) { 182 checkServiceBinder(); 183 if (sService != null) { 184 try { 185 sService.dataChanged(packageName); 186 } catch (RemoteException e) { 187 Log.e(TAG, "dataChanged(pkg) couldn't connect"); 188 } 189 } 190 } 191 192 /** 193 * Restore the calling application from backup. The data will be restored from the 194 * current backup dataset if the application has stored data there, or from 195 * the dataset used during the last full device setup operation if the current 196 * backup dataset has no matching data. If no backup data exists for this application 197 * in either source, a nonzero value will be returned. 198 * 199 * <p>If this method returns zero (meaning success), the OS will attempt to retrieve 200 * a backed-up dataset from the remote transport, instantiate the application's 201 * backup agent, and pass the dataset to the agent's 202 * {@link android.app.backup.BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) onRestore()} 203 * method. 204 * 205 * @param observer The {@link RestoreObserver} to receive callbacks during the restore 206 * operation. This must not be null. 207 * 208 * @return Zero on success; nonzero on error. 209 */ requestRestore(RestoreObserver observer)210 public int requestRestore(RestoreObserver observer) { 211 int result = -1; 212 checkServiceBinder(); 213 if (sService != null) { 214 RestoreSession session = null; 215 try { 216 IRestoreSession binder = sService.beginRestoreSession(mContext.getPackageName(), 217 null); 218 if (binder != null) { 219 session = new RestoreSession(mContext, binder); 220 result = session.restorePackage(mContext.getPackageName(), observer); 221 } 222 } catch (RemoteException e) { 223 Log.e(TAG, "restoreSelf() unable to contact service"); 224 } finally { 225 if (session != null) { 226 session.endRestoreSession(); 227 } 228 } 229 } 230 return result; 231 } 232 233 // system APIs start here 234 235 /** 236 * Begin the process of restoring data from backup. See the 237 * {@link android.app.backup.RestoreSession} class for documentation on that process. 238 * @hide 239 */ 240 @SystemApi beginRestoreSession()241 public RestoreSession beginRestoreSession() { 242 RestoreSession session = null; 243 checkServiceBinder(); 244 if (sService != null) { 245 try { 246 // All packages, current transport 247 IRestoreSession binder = sService.beginRestoreSession(null, null); 248 if (binder != null) { 249 session = new RestoreSession(mContext, binder); 250 } 251 } catch (RemoteException e) { 252 Log.e(TAG, "beginRestoreSession() couldn't connect"); 253 } 254 } 255 return session; 256 } 257 258 /** 259 * Enable/disable the backup service entirely. When disabled, no backup 260 * or restore operations will take place. Data-changed notifications will 261 * still be observed and collected, however, so that changes made while the 262 * mechanism was disabled will still be backed up properly if it is enabled 263 * at some point in the future. 264 * 265 * <p>Callers must hold the android.permission.BACKUP permission to use this method. 266 * 267 * @hide 268 */ 269 @SystemApi setBackupEnabled(boolean isEnabled)270 public void setBackupEnabled(boolean isEnabled) { 271 checkServiceBinder(); 272 if (sService != null) { 273 try { 274 sService.setBackupEnabled(isEnabled); 275 } catch (RemoteException e) { 276 Log.e(TAG, "setBackupEnabled() couldn't connect"); 277 } 278 } 279 } 280 281 /** 282 * Report whether the backup mechanism is currently enabled. 283 * 284 * <p>Callers must hold the android.permission.BACKUP permission to use this method. 285 * 286 * @hide 287 */ 288 @SystemApi isBackupEnabled()289 public boolean isBackupEnabled() { 290 checkServiceBinder(); 291 if (sService != null) { 292 try { 293 return sService.isBackupEnabled(); 294 } catch (RemoteException e) { 295 Log.e(TAG, "isBackupEnabled() couldn't connect"); 296 } 297 } 298 return false; 299 } 300 301 /** 302 * Enable/disable data restore at application install time. When enabled, app 303 * installation will include an attempt to fetch the app's historical data from 304 * the archival restore dataset (if any). When disabled, no such attempt will 305 * be made. 306 * 307 * <p>Callers must hold the android.permission.BACKUP permission to use this method. 308 * 309 * @hide 310 */ 311 @SystemApi setAutoRestore(boolean isEnabled)312 public void setAutoRestore(boolean isEnabled) { 313 checkServiceBinder(); 314 if (sService != null) { 315 try { 316 sService.setAutoRestore(isEnabled); 317 } catch (RemoteException e) { 318 Log.e(TAG, "setAutoRestore() couldn't connect"); 319 } 320 } 321 } 322 323 /** 324 * Identify the currently selected transport. Callers must hold the 325 * android.permission.BACKUP permission to use this method. 326 * @return The name of the currently active backup transport. In case of 327 * failure or if no transport is currently active, this method returns {@code null}. 328 * 329 * @hide 330 */ 331 @SystemApi getCurrentTransport()332 public String getCurrentTransport() { 333 checkServiceBinder(); 334 if (sService != null) { 335 try { 336 return sService.getCurrentTransport(); 337 } catch (RemoteException e) { 338 Log.e(TAG, "getCurrentTransport() couldn't connect"); 339 } 340 } 341 return null; 342 } 343 344 /** 345 * Request a list of all available backup transports' names. Callers must 346 * hold the android.permission.BACKUP permission to use this method. 347 * 348 * @hide 349 */ 350 @SystemApi listAllTransports()351 public String[] listAllTransports() { 352 checkServiceBinder(); 353 if (sService != null) { 354 try { 355 return sService.listAllTransports(); 356 } catch (RemoteException e) { 357 Log.e(TAG, "listAllTransports() couldn't connect"); 358 } 359 } 360 return null; 361 } 362 363 /** 364 * Specify the current backup transport. Callers must hold the 365 * android.permission.BACKUP permission to use this method. 366 * 367 * @param transport The name of the transport to select. This should be one 368 * of the names returned by {@link #listAllTransports()}. 369 * @return The name of the previously selected transport. If the given transport 370 * name is not one of the currently available transports, no change is made to 371 * the current transport setting and the method returns null. 372 * 373 * @hide 374 */ 375 @SystemApi selectBackupTransport(String transport)376 public String selectBackupTransport(String transport) { 377 checkServiceBinder(); 378 if (sService != null) { 379 try { 380 return sService.selectBackupTransport(transport); 381 } catch (RemoteException e) { 382 Log.e(TAG, "selectBackupTransport() couldn't connect"); 383 } 384 } 385 return null; 386 } 387 388 /** 389 * Schedule an immediate backup attempt for all pending key/value updates. This 390 * is primarily intended for transports to use when they detect a suitable 391 * opportunity for doing a backup pass. If there are no pending updates to 392 * be sent, no action will be taken. Even if some updates are pending, the 393 * transport will still be asked to confirm via the usual requestBackupTime() 394 * method. 395 * 396 * <p>Callers must hold the android.permission.BACKUP permission to use this method. 397 * 398 * @hide 399 */ 400 @SystemApi backupNow()401 public void backupNow() { 402 checkServiceBinder(); 403 if (sService != null) { 404 try { 405 sService.backupNow(); 406 } catch (RemoteException e) { 407 Log.e(TAG, "backupNow() couldn't connect"); 408 } 409 } 410 } 411 412 /** 413 * Ask the framework which dataset, if any, the given package's data would be 414 * restored from if we were to install it right now. 415 * 416 * <p>Callers must hold the android.permission.BACKUP permission to use this method. 417 * 418 * @param packageName The name of the package whose most-suitable dataset we 419 * wish to look up 420 * @return The dataset token from which a restore should be attempted, or zero if 421 * no suitable data is available. 422 * 423 * @hide 424 */ 425 @SystemApi getAvailableRestoreToken(String packageName)426 public long getAvailableRestoreToken(String packageName) { 427 checkServiceBinder(); 428 if (sService != null) { 429 try { 430 return sService.getAvailableRestoreToken(packageName); 431 } catch (RemoteException e) { 432 Log.e(TAG, "getAvailableRestoreToken() couldn't connect"); 433 } 434 } 435 return 0; 436 } 437 438 /** 439 * Ask the framework whether this app is eligible for backup. 440 * 441 * <p>Callers must hold the android.permission.BACKUP permission to use this method. 442 * 443 * @param packageName The name of the package. 444 * @return Whether this app is eligible for backup. 445 * 446 * @hide 447 */ 448 @SystemApi isAppEligibleForBackup(String packageName)449 public boolean isAppEligibleForBackup(String packageName) { 450 checkServiceBinder(); 451 if (sService != null) { 452 try { 453 return sService.isAppEligibleForBackup(packageName); 454 } catch (RemoteException e) { 455 Log.e(TAG, "isAppEligibleForBackup(pkg) couldn't connect"); 456 } 457 } 458 return false; 459 } 460 461 /** 462 * Request an immediate backup, providing an observer to which results of the backup operation 463 * will be published. The Android backup system will decide for each package whether it will 464 * be full app data backup or key/value-pair-based backup. 465 * 466 * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all 467 * provided packages using the remote transport. 468 * 469 * @param packages List of package names to backup. 470 * @param observer The {@link BackupObserver} to receive callbacks during the backup 471 * operation. Could be {@code null}. 472 * @return {@link BackupManager#SUCCESS} on success; nonzero on error. 473 * @exception IllegalArgumentException on null or empty {@code packages} param. 474 * 475 * @hide 476 */ 477 @SystemApi requestBackup(String[] packages, BackupObserver observer)478 public int requestBackup(String[] packages, BackupObserver observer) { 479 checkServiceBinder(); 480 if (sService != null) { 481 try { 482 BackupObserverWrapper observerWrapper = observer == null 483 ? null 484 : new BackupObserverWrapper(mContext, observer); 485 return sService.requestBackup(packages, observerWrapper); 486 } catch (RemoteException e) { 487 Log.e(TAG, "requestBackup() couldn't connect"); 488 } 489 } 490 return -1; 491 } 492 493 /* 494 * We wrap incoming binder calls with a private class implementation that 495 * redirects them into main-thread actions. This serializes the backup 496 * progress callbacks nicely within the usual main-thread lifecycle pattern. 497 */ 498 @SystemApi 499 private class BackupObserverWrapper extends IBackupObserver.Stub { 500 final Handler mHandler; 501 final BackupObserver mObserver; 502 503 static final int MSG_UPDATE = 1; 504 static final int MSG_RESULT = 2; 505 static final int MSG_FINISHED = 3; 506 BackupObserverWrapper(Context context, BackupObserver observer)507 BackupObserverWrapper(Context context, BackupObserver observer) { 508 mHandler = new Handler(context.getMainLooper()) { 509 @Override 510 public void handleMessage(Message msg) { 511 switch (msg.what) { 512 case MSG_UPDATE: 513 Pair<String, BackupProgress> obj = 514 (Pair<String, BackupProgress>) msg.obj; 515 mObserver.onUpdate(obj.first, obj.second); 516 break; 517 case MSG_RESULT: 518 mObserver.onResult((String)msg.obj, msg.arg1); 519 break; 520 case MSG_FINISHED: 521 mObserver.backupFinished(msg.arg1); 522 break; 523 default: 524 Log.w(TAG, "Unknown message: " + msg); 525 break; 526 } 527 } 528 }; 529 mObserver = observer; 530 } 531 532 // Binder calls into this object just enqueue on the main-thread handler 533 @Override onUpdate(String currentPackage, BackupProgress backupProgress)534 public void onUpdate(String currentPackage, BackupProgress backupProgress) { 535 mHandler.sendMessage( 536 mHandler.obtainMessage(MSG_UPDATE, Pair.create(currentPackage, backupProgress))); 537 } 538 539 @Override onResult(String currentPackage, int status)540 public void onResult(String currentPackage, int status) { 541 mHandler.sendMessage( 542 mHandler.obtainMessage(MSG_RESULT, status, 0, currentPackage)); 543 } 544 545 @Override backupFinished(int status)546 public void backupFinished(int status) { 547 mHandler.sendMessage( 548 mHandler.obtainMessage(MSG_FINISHED, status, 0)); 549 } 550 } 551 } 552