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 /** 132 * Intent extra when any subsidiary backup-related UI is launched from Settings: does 133 * device policy or configuration permit backup operations to run at all? 134 * 135 * @hide 136 */ 137 public static final String EXTRA_BACKUP_SERVICES_AVAILABLE = "backup_services_available"; 138 139 private Context mContext; 140 private static IBackupManager sService; 141 checkServiceBinder()142 private static void checkServiceBinder() { 143 if (sService == null) { 144 sService = IBackupManager.Stub.asInterface( 145 ServiceManager.getService(Context.BACKUP_SERVICE)); 146 } 147 } 148 149 /** 150 * Constructs a BackupManager object through which the application can 151 * communicate with the Android backup system. 152 * 153 * @param context The {@link android.content.Context} that was provided when 154 * one of your application's {@link android.app.Activity Activities} 155 * was created. 156 */ BackupManager(Context context)157 public BackupManager(Context context) { 158 mContext = context; 159 } 160 161 /** 162 * Notifies the Android backup system that your application wishes to back up 163 * new changes to its data. A backup operation using your application's 164 * {@link android.app.backup.BackupAgent} subclass will be scheduled when you 165 * call this method. 166 */ dataChanged()167 public void dataChanged() { 168 checkServiceBinder(); 169 if (sService != null) { 170 try { 171 sService.dataChanged(mContext.getPackageName()); 172 } catch (RemoteException e) { 173 Log.d(TAG, "dataChanged() couldn't connect"); 174 } 175 } 176 } 177 178 /** 179 * Convenience method for callers who need to indicate that some other package 180 * needs a backup pass. This can be useful in the case of groups of packages 181 * that share a uid. 182 * <p> 183 * This method requires that the application hold the "android.permission.BACKUP" 184 * permission if the package named in the argument does not run under the same uid 185 * as the caller. 186 * 187 * @param packageName The package name identifying the application to back up. 188 */ dataChanged(String packageName)189 public static void dataChanged(String packageName) { 190 checkServiceBinder(); 191 if (sService != null) { 192 try { 193 sService.dataChanged(packageName); 194 } catch (RemoteException e) { 195 Log.e(TAG, "dataChanged(pkg) couldn't connect"); 196 } 197 } 198 } 199 200 /** 201 * Restore the calling application from backup. The data will be restored from the 202 * current backup dataset if the application has stored data there, or from 203 * the dataset used during the last full device setup operation if the current 204 * backup dataset has no matching data. If no backup data exists for this application 205 * in either source, a nonzero value will be returned. 206 * 207 * <p>If this method returns zero (meaning success), the OS will attempt to retrieve 208 * a backed-up dataset from the remote transport, instantiate the application's 209 * backup agent, and pass the dataset to the agent's 210 * {@link android.app.backup.BackupAgent#onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor) onRestore()} 211 * method. 212 * 213 * @param observer The {@link RestoreObserver} to receive callbacks during the restore 214 * operation. This must not be null. 215 * 216 * @return Zero on success; nonzero on error. 217 */ requestRestore(RestoreObserver observer)218 public int requestRestore(RestoreObserver observer) { 219 int result = -1; 220 checkServiceBinder(); 221 if (sService != null) { 222 RestoreSession session = null; 223 try { 224 IRestoreSession binder = sService.beginRestoreSession(mContext.getPackageName(), 225 null); 226 if (binder != null) { 227 session = new RestoreSession(mContext, binder); 228 result = session.restorePackage(mContext.getPackageName(), observer); 229 } 230 } catch (RemoteException e) { 231 Log.e(TAG, "restoreSelf() unable to contact service"); 232 } finally { 233 if (session != null) { 234 session.endRestoreSession(); 235 } 236 } 237 } 238 return result; 239 } 240 241 // system APIs start here 242 243 /** 244 * Begin the process of restoring data from backup. See the 245 * {@link android.app.backup.RestoreSession} class for documentation on that process. 246 * @hide 247 */ 248 @SystemApi beginRestoreSession()249 public RestoreSession beginRestoreSession() { 250 RestoreSession session = null; 251 checkServiceBinder(); 252 if (sService != null) { 253 try { 254 // All packages, current transport 255 IRestoreSession binder = sService.beginRestoreSession(null, null); 256 if (binder != null) { 257 session = new RestoreSession(mContext, binder); 258 } 259 } catch (RemoteException e) { 260 Log.e(TAG, "beginRestoreSession() couldn't connect"); 261 } 262 } 263 return session; 264 } 265 266 /** 267 * Enable/disable the backup service entirely. When disabled, no backup 268 * or restore operations will take place. Data-changed notifications will 269 * still be observed and collected, however, so that changes made while the 270 * mechanism was disabled will still be backed up properly if it is enabled 271 * at some point in the future. 272 * 273 * <p>Callers must hold the android.permission.BACKUP permission to use this method. 274 * 275 * @hide 276 */ 277 @SystemApi setBackupEnabled(boolean isEnabled)278 public void setBackupEnabled(boolean isEnabled) { 279 checkServiceBinder(); 280 if (sService != null) { 281 try { 282 sService.setBackupEnabled(isEnabled); 283 } catch (RemoteException e) { 284 Log.e(TAG, "setBackupEnabled() couldn't connect"); 285 } 286 } 287 } 288 289 /** 290 * Report whether the backup mechanism is currently enabled. 291 * 292 * <p>Callers must hold the android.permission.BACKUP permission to use this method. 293 * 294 * @hide 295 */ 296 @SystemApi isBackupEnabled()297 public boolean isBackupEnabled() { 298 checkServiceBinder(); 299 if (sService != null) { 300 try { 301 return sService.isBackupEnabled(); 302 } catch (RemoteException e) { 303 Log.e(TAG, "isBackupEnabled() couldn't connect"); 304 } 305 } 306 return false; 307 } 308 309 /** 310 * Enable/disable data restore at application install time. When enabled, app 311 * installation will include an attempt to fetch the app's historical data from 312 * the archival restore dataset (if any). When disabled, no such attempt will 313 * be made. 314 * 315 * <p>Callers must hold the android.permission.BACKUP permission to use this method. 316 * 317 * @hide 318 */ 319 @SystemApi setAutoRestore(boolean isEnabled)320 public void setAutoRestore(boolean isEnabled) { 321 checkServiceBinder(); 322 if (sService != null) { 323 try { 324 sService.setAutoRestore(isEnabled); 325 } catch (RemoteException e) { 326 Log.e(TAG, "setAutoRestore() couldn't connect"); 327 } 328 } 329 } 330 331 /** 332 * Identify the currently selected transport. Callers must hold the 333 * android.permission.BACKUP permission to use this method. 334 * @return The name of the currently active backup transport. In case of 335 * failure or if no transport is currently active, this method returns {@code null}. 336 * 337 * @hide 338 */ 339 @SystemApi getCurrentTransport()340 public String getCurrentTransport() { 341 checkServiceBinder(); 342 if (sService != null) { 343 try { 344 return sService.getCurrentTransport(); 345 } catch (RemoteException e) { 346 Log.e(TAG, "getCurrentTransport() couldn't connect"); 347 } 348 } 349 return null; 350 } 351 352 /** 353 * Request a list of all available backup transports' names. Callers must 354 * hold the android.permission.BACKUP permission to use this method. 355 * 356 * @hide 357 */ 358 @SystemApi listAllTransports()359 public String[] listAllTransports() { 360 checkServiceBinder(); 361 if (sService != null) { 362 try { 363 return sService.listAllTransports(); 364 } catch (RemoteException e) { 365 Log.e(TAG, "listAllTransports() couldn't connect"); 366 } 367 } 368 return null; 369 } 370 371 /** 372 * Specify the current backup transport. Callers must hold the 373 * android.permission.BACKUP permission to use this method. 374 * 375 * @param transport The name of the transport to select. This should be one 376 * of the names returned by {@link #listAllTransports()}. 377 * @return The name of the previously selected transport. If the given transport 378 * name is not one of the currently available transports, no change is made to 379 * the current transport setting and the method returns null. 380 * 381 * @hide 382 */ 383 @SystemApi selectBackupTransport(String transport)384 public String selectBackupTransport(String transport) { 385 checkServiceBinder(); 386 if (sService != null) { 387 try { 388 return sService.selectBackupTransport(transport); 389 } catch (RemoteException e) { 390 Log.e(TAG, "selectBackupTransport() couldn't connect"); 391 } 392 } 393 return null; 394 } 395 396 /** 397 * Schedule an immediate backup attempt for all pending key/value updates. This 398 * is primarily intended for transports to use when they detect a suitable 399 * opportunity for doing a backup pass. If there are no pending updates to 400 * be sent, no action will be taken. Even if some updates are pending, the 401 * transport will still be asked to confirm via the usual requestBackupTime() 402 * method. 403 * 404 * <p>Callers must hold the android.permission.BACKUP permission to use this method. 405 * 406 * @hide 407 */ 408 @SystemApi backupNow()409 public void backupNow() { 410 checkServiceBinder(); 411 if (sService != null) { 412 try { 413 sService.backupNow(); 414 } catch (RemoteException e) { 415 Log.e(TAG, "backupNow() couldn't connect"); 416 } 417 } 418 } 419 420 /** 421 * Ask the framework which dataset, if any, the given package's data would be 422 * restored from if we were to install it right now. 423 * 424 * <p>Callers must hold the android.permission.BACKUP permission to use this method. 425 * 426 * @param packageName The name of the package whose most-suitable dataset we 427 * wish to look up 428 * @return The dataset token from which a restore should be attempted, or zero if 429 * no suitable data is available. 430 * 431 * @hide 432 */ 433 @SystemApi getAvailableRestoreToken(String packageName)434 public long getAvailableRestoreToken(String packageName) { 435 checkServiceBinder(); 436 if (sService != null) { 437 try { 438 return sService.getAvailableRestoreToken(packageName); 439 } catch (RemoteException e) { 440 Log.e(TAG, "getAvailableRestoreToken() couldn't connect"); 441 } 442 } 443 return 0; 444 } 445 446 /** 447 * Ask the framework whether this app is eligible for backup. 448 * 449 * <p>Callers must hold the android.permission.BACKUP permission to use this method. 450 * 451 * @param packageName The name of the package. 452 * @return Whether this app is eligible for backup. 453 * 454 * @hide 455 */ 456 @SystemApi isAppEligibleForBackup(String packageName)457 public boolean isAppEligibleForBackup(String packageName) { 458 checkServiceBinder(); 459 if (sService != null) { 460 try { 461 return sService.isAppEligibleForBackup(packageName); 462 } catch (RemoteException e) { 463 Log.e(TAG, "isAppEligibleForBackup(pkg) couldn't connect"); 464 } 465 } 466 return false; 467 } 468 469 /** 470 * Request an immediate backup, providing an observer to which results of the backup operation 471 * will be published. The Android backup system will decide for each package whether it will 472 * be full app data backup or key/value-pair-based backup. 473 * 474 * <p>If this method returns {@link BackupManager#SUCCESS}, the OS will attempt to backup all 475 * provided packages using the remote transport. 476 * 477 * @param packages List of package names to backup. 478 * @param observer The {@link BackupObserver} to receive callbacks during the backup 479 * operation. Could be {@code null}. 480 * @return {@link BackupManager#SUCCESS} on success; nonzero on error. 481 * @exception IllegalArgumentException on null or empty {@code packages} param. 482 * 483 * @hide 484 */ 485 @SystemApi requestBackup(String[] packages, BackupObserver observer)486 public int requestBackup(String[] packages, BackupObserver observer) { 487 checkServiceBinder(); 488 if (sService != null) { 489 try { 490 BackupObserverWrapper observerWrapper = observer == null 491 ? null 492 : new BackupObserverWrapper(mContext, observer); 493 return sService.requestBackup(packages, observerWrapper); 494 } catch (RemoteException e) { 495 Log.e(TAG, "requestBackup() couldn't connect"); 496 } 497 } 498 return -1; 499 } 500 501 /* 502 * We wrap incoming binder calls with a private class implementation that 503 * redirects them into main-thread actions. This serializes the backup 504 * progress callbacks nicely within the usual main-thread lifecycle pattern. 505 */ 506 @SystemApi 507 private class BackupObserverWrapper extends IBackupObserver.Stub { 508 final Handler mHandler; 509 final BackupObserver mObserver; 510 511 static final int MSG_UPDATE = 1; 512 static final int MSG_RESULT = 2; 513 static final int MSG_FINISHED = 3; 514 BackupObserverWrapper(Context context, BackupObserver observer)515 BackupObserverWrapper(Context context, BackupObserver observer) { 516 mHandler = new Handler(context.getMainLooper()) { 517 @Override 518 public void handleMessage(Message msg) { 519 switch (msg.what) { 520 case MSG_UPDATE: 521 Pair<String, BackupProgress> obj = 522 (Pair<String, BackupProgress>) msg.obj; 523 mObserver.onUpdate(obj.first, obj.second); 524 break; 525 case MSG_RESULT: 526 mObserver.onResult((String)msg.obj, msg.arg1); 527 break; 528 case MSG_FINISHED: 529 mObserver.backupFinished(msg.arg1); 530 break; 531 default: 532 Log.w(TAG, "Unknown message: " + msg); 533 break; 534 } 535 } 536 }; 537 mObserver = observer; 538 } 539 540 // Binder calls into this object just enqueue on the main-thread handler 541 @Override onUpdate(String currentPackage, BackupProgress backupProgress)542 public void onUpdate(String currentPackage, BackupProgress backupProgress) { 543 mHandler.sendMessage( 544 mHandler.obtainMessage(MSG_UPDATE, Pair.create(currentPackage, backupProgress))); 545 } 546 547 @Override onResult(String currentPackage, int status)548 public void onResult(String currentPackage, int status) { 549 mHandler.sendMessage( 550 mHandler.obtainMessage(MSG_RESULT, status, 0, currentPackage)); 551 } 552 553 @Override backupFinished(int status)554 public void backupFinished(int status) { 555 mHandler.sendMessage( 556 mHandler.obtainMessage(MSG_FINISHED, status, 0)); 557 } 558 } 559 } 560