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.QueuedWork; 21 import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags; 22 import android.content.Context; 23 import android.content.ContextWrapper; 24 import android.content.pm.ApplicationInfo; 25 import android.os.Binder; 26 import android.os.Handler; 27 import android.os.IBinder; 28 import android.os.Looper; 29 import android.os.ParcelFileDescriptor; 30 import android.os.Process; 31 import android.os.RemoteException; 32 import android.system.ErrnoException; 33 import android.system.Os; 34 import android.system.OsConstants; 35 import android.system.StructStat; 36 import android.util.ArraySet; 37 import android.util.Log; 38 39 import libcore.io.IoUtils; 40 41 import org.xmlpull.v1.XmlPullParserException; 42 43 import java.io.File; 44 import java.io.FileOutputStream; 45 import java.io.IOException; 46 import java.util.Collection; 47 import java.util.LinkedList; 48 import java.util.Map; 49 import java.util.Set; 50 import java.util.concurrent.CountDownLatch; 51 52 /** 53 * Provides the central interface between an 54 * application and Android's data backup infrastructure. An application that wishes 55 * to participate in the backup and restore mechanism will declare a subclass of 56 * {@link android.app.backup.BackupAgent}, implement the 57 * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()} 58 * and {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} methods, 59 * and provide the name of its backup agent class in its {@code AndroidManifest.xml} file via 60 * the <code> 61 * <a href="{@docRoot}guide/topics/manifest/application-element.html"><application></a></code> 62 * tag's {@code android:backupAgent} attribute. 63 * 64 * <div class="special reference"> 65 * <h3>Developer Guides</h3> 66 * <p>For more information about using BackupAgent, read the 67 * <a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a> developer guide.</p></div> 68 * 69 * <h3>Basic Operation</h3> 70 * <p> 71 * When the application makes changes to data that it wishes to keep backed up, 72 * it should call the 73 * {@link android.app.backup.BackupManager#dataChanged() BackupManager.dataChanged()} method. 74 * This notifies the Android Backup Manager that the application needs an opportunity 75 * to update its backup image. The Backup Manager, in turn, schedules a 76 * backup pass to be performed at an opportune time. 77 * <p> 78 * Restore operations are typically performed only when applications are first 79 * installed on a device. At that time, the operating system checks to see whether 80 * there is a previously-saved data set available for the application being installed, and if so, 81 * begins an immediate restore pass to deliver the backup data as part of the installation 82 * process. 83 * <p> 84 * When a backup or restore pass is run, the application's process is launched 85 * (if not already running), the manifest-declared backup agent class (in the {@code 86 * android:backupAgent} attribute) is instantiated within 87 * that process, and the agent's {@link #onCreate()} method is invoked. This prepares the 88 * agent instance to run the actual backup or restore logic. At this point the 89 * agent's 90 * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()} or 91 * {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} method will be 92 * invoked as appropriate for the operation being performed. 93 * <p> 94 * A backup data set consists of one or more "entities," flattened binary data 95 * records that are each identified with a key string unique within the data set. Adding a 96 * record to the active data set or updating an existing record is done by simply 97 * writing new entity data under the desired key. Deleting an entity from the data set 98 * is done by writing an entity under that key with header specifying a negative data 99 * size, and no actual entity data. 100 * <p> 101 * <b>Helper Classes</b> 102 * <p> 103 * An extensible agent based on convenient helper classes is available in 104 * {@link android.app.backup.BackupAgentHelper}. That class is particularly 105 * suited to handling of simple file or {@link android.content.SharedPreferences} 106 * backup and restore. 107 * <p> 108 * <b>Threading</b> 109 * <p> 110 * The constructor, as well as {@link #onCreate()} and {@link #onDestroy()} lifecycle callbacks run 111 * on the main thread (UI thread) of the application that implements the BackupAgent. 112 * The data-handling callbacks: 113 * {@link #onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()}, 114 * {@link #onFullBackup(FullBackupDataOutput)}, 115 * {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()}, 116 * {@link #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long) onRestoreFile()}, 117 * {@link #onRestoreFinished()}, and {@link #onQuotaExceeded(long, long) onQuotaExceeded()} 118 * run on binder pool threads. 119 * 120 * @see android.app.backup.BackupManager 121 * @see android.app.backup.BackupAgentHelper 122 * @see android.app.backup.BackupDataInput 123 * @see android.app.backup.BackupDataOutput 124 */ 125 public abstract class BackupAgent extends ContextWrapper { 126 private static final String TAG = "BackupAgent"; 127 private static final boolean DEBUG = false; 128 129 /** @hide */ 130 public static final int TYPE_EOF = 0; 131 132 /** 133 * During a full restore, indicates that the file system object being restored 134 * is an ordinary file. 135 */ 136 public static final int TYPE_FILE = 1; 137 138 /** 139 * During a full restore, indicates that the file system object being restored 140 * is a directory. 141 */ 142 public static final int TYPE_DIRECTORY = 2; 143 144 /** @hide */ 145 public static final int TYPE_SYMLINK = 3; 146 147 /** 148 * Flag for {@link BackupDataOutput#getTransportFlags()} and 149 * {@link FullBackupDataOutput#getTransportFlags()} only. 150 * 151 * <p>The transport has client-side encryption enabled. i.e., the user's backup has been 152 * encrypted with a key known only to the device, and not to the remote storage solution. Even 153 * if an attacker had root access to the remote storage provider they should not be able to 154 * decrypt the user's backup data. 155 */ 156 public static final int FLAG_CLIENT_SIDE_ENCRYPTION_ENABLED = 1; 157 158 /** 159 * Flag for {@link BackupDataOutput#getTransportFlags()} and 160 * {@link FullBackupDataOutput#getTransportFlags()} only. 161 * 162 * <p>The transport is for a device-to-device transfer. There is no third party or intermediate 163 * storage. The user's backup data is sent directly to another device over e.g., USB or WiFi. 164 */ 165 public static final int FLAG_DEVICE_TO_DEVICE_TRANSFER = 2; 166 167 /** 168 * Flag for {@link BackupDataOutput#getTransportFlags()} and 169 * {@link FullBackupDataOutput#getTransportFlags()} only. 170 * 171 * <p>Used for internal testing only. Do not check this flag in production code. 172 * 173 * @hide 174 */ 175 public static final int FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED = 1 << 31; 176 177 Handler mHandler = null; 178 179 Handler getHandler() { 180 if (mHandler == null) { 181 mHandler = new Handler(Looper.getMainLooper()); 182 } 183 return mHandler; 184 } 185 186 class SharedPrefsSynchronizer implements Runnable { 187 public final CountDownLatch mLatch = new CountDownLatch(1); 188 189 @Override 190 public void run() { 191 QueuedWork.waitToFinish(); 192 mLatch.countDown(); 193 } 194 }; 195 196 // Syncing shared preferences deferred writes needs to happen on the main looper thread 197 private void waitForSharedPrefs() { 198 Handler h = getHandler(); 199 final SharedPrefsSynchronizer s = new SharedPrefsSynchronizer(); 200 h.postAtFrontOfQueue(s); 201 try { 202 s.mLatch.await(); 203 } catch (InterruptedException e) { /* ignored */ } 204 } 205 206 207 public BackupAgent() { 208 super(null); 209 } 210 211 /** 212 * Provided as a convenience for agent implementations that need an opportunity 213 * to do one-time initialization before the actual backup or restore operation 214 * is begun. 215 * <p> 216 */ 217 public void onCreate() { 218 } 219 220 /** 221 * Provided as a convenience for agent implementations that need to do some 222 * sort of shutdown process after backup or restore is completed. 223 * <p> 224 * Agents do not need to override this method. 225 */ 226 public void onDestroy() { 227 } 228 229 /** 230 * The application is being asked to write any data changed since the last 231 * time it performed a backup operation. The state data recorded during the 232 * last backup pass is provided in the <code>oldState</code> file 233 * descriptor. If <code>oldState</code> is <code>null</code>, no old state 234 * is available and the application should perform a full backup. In both 235 * cases, a representation of the final backup state after this pass should 236 * be written to the file pointed to by the file descriptor wrapped in 237 * <code>newState</code>. 238 * <p> 239 * Each entity written to the {@link android.app.backup.BackupDataOutput} 240 * <code>data</code> stream will be transmitted 241 * over the current backup transport and stored in the remote data set under 242 * the key supplied as part of the entity. Writing an entity with a negative 243 * data size instructs the transport to delete whatever entity currently exists 244 * under that key from the remote data set. 245 * 246 * @param oldState An open, read-only ParcelFileDescriptor pointing to the 247 * last backup state provided by the application. May be 248 * <code>null</code>, in which case no prior state is being 249 * provided and the application should perform a full backup. 250 * @param data A structured wrapper around an open, read/write 251 * file descriptor pointing to the backup data destination. 252 * Typically the application will use backup helper classes to 253 * write to this file. 254 * @param newState An open, read/write ParcelFileDescriptor pointing to an 255 * empty file. The application should record the final backup 256 * state here after writing the requested data to the <code>data</code> 257 * output stream. 258 */ 259 public abstract void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, 260 ParcelFileDescriptor newState) throws IOException; 261 262 /** 263 * The application is being restored from backup and should replace any 264 * existing data with the contents of the backup. The backup data is 265 * provided through the <code>data</code> parameter. Once 266 * the restore is finished, the application should write a representation of 267 * the final state to the <code>newState</code> file descriptor. 268 * <p> 269 * The application is responsible for properly erasing its old data and 270 * replacing it with the data supplied to this method. No "clear user data" 271 * operation will be performed automatically by the operating system. The 272 * exception to this is in the case of a failed restore attempt: if 273 * onRestore() throws an exception, the OS will assume that the 274 * application's data may now be in an incoherent state, and will clear it 275 * before proceeding. 276 * 277 * @param data A structured wrapper around an open, read-only 278 * file descriptor pointing to a full snapshot of the 279 * application's data. The application should consume every 280 * entity represented in this data stream. 281 * @param appVersionCode The value of the <a 282 * href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code 283 * android:versionCode}</a> manifest attribute, 284 * from the application that backed up this particular data set. This 285 * makes it possible for an application's agent to distinguish among any 286 * possible older data versions when asked to perform the restore 287 * operation. 288 * @param newState An open, read/write ParcelFileDescriptor pointing to an 289 * empty file. The application should record the final backup 290 * state here after restoring its data from the <code>data</code> stream. 291 * When a full-backup dataset is being restored, this will be <code>null</code>. 292 */ 293 public abstract void onRestore(BackupDataInput data, int appVersionCode, 294 ParcelFileDescriptor newState) throws IOException; 295 296 /** 297 * New version of {@link #onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)} 298 * that handles a long app version code. Default implementation casts the version code to 299 * an int and calls {@link #onRestore(BackupDataInput, int, android.os.ParcelFileDescriptor)}. 300 */ 301 public void onRestore(BackupDataInput data, long appVersionCode, 302 ParcelFileDescriptor newState) 303 throws IOException { 304 onRestore(data, (int) appVersionCode, newState); 305 } 306 307 /** 308 * The application is having its entire file system contents backed up. {@code data} 309 * points to the backup destination, and the app has the opportunity to choose which 310 * files are to be stored. To commit a file as part of the backup, call the 311 * {@link #fullBackupFile(File, FullBackupDataOutput)} helper method. After all file 312 * data is written to the output, the agent returns from this method and the backup 313 * operation concludes. 314 * 315 * <p>Certain parts of the app's data are never backed up even if the app explicitly 316 * sends them to the output: 317 * 318 * <ul> 319 * <li>The contents of the {@link #getCacheDir()} directory</li> 320 * <li>The contents of the {@link #getCodeCacheDir()} directory</li> 321 * <li>The contents of the {@link #getNoBackupFilesDir()} directory</li> 322 * <li>The contents of the app's shared library directory</li> 323 * </ul> 324 * 325 * <p>The default implementation of this method backs up the entirety of the 326 * application's "owned" file system trees to the output other than the few exceptions 327 * listed above. Apps only need to override this method if they need to impose special 328 * limitations on which files are being stored beyond the control that 329 * {@link #getNoBackupFilesDir()} offers. 330 * Alternatively they can provide an xml resource to specify what data to include or exclude. 331 * 332 * 333 * @param data A structured wrapper pointing to the backup destination. 334 * @throws IOException 335 * 336 * @see Context#getNoBackupFilesDir() 337 * @see ApplicationInfo#fullBackupContent 338 * @see #fullBackupFile(File, FullBackupDataOutput) 339 * @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long) 340 */ 341 public void onFullBackup(FullBackupDataOutput data) throws IOException { 342 FullBackup.BackupScheme backupScheme = FullBackup.getBackupScheme(this); 343 if (!backupScheme.isFullBackupContentEnabled()) { 344 return; 345 } 346 347 Map<String, Set<PathWithRequiredFlags>> manifestIncludeMap; 348 ArraySet<PathWithRequiredFlags> manifestExcludeSet; 349 try { 350 manifestIncludeMap = 351 backupScheme.maybeParseAndGetCanonicalIncludePaths(); 352 manifestExcludeSet = backupScheme.maybeParseAndGetCanonicalExcludePaths(); 353 } catch (IOException | XmlPullParserException e) { 354 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { 355 Log.v(FullBackup.TAG_XML_PARSER, 356 "Exception trying to parse fullBackupContent xml file!" 357 + " Aborting full backup.", e); 358 } 359 return; 360 } 361 362 final String packageName = getPackageName(); 363 final ApplicationInfo appInfo = getApplicationInfo(); 364 365 // System apps have control over where their default storage context 366 // is pointed, so we're always explicit when building paths. 367 final Context ceContext = createCredentialProtectedStorageContext(); 368 final String rootDir = ceContext.getDataDir().getCanonicalPath(); 369 final String filesDir = ceContext.getFilesDir().getCanonicalPath(); 370 final String noBackupDir = ceContext.getNoBackupFilesDir().getCanonicalPath(); 371 final String databaseDir = ceContext.getDatabasePath("foo").getParentFile() 372 .getCanonicalPath(); 373 final String sharedPrefsDir = ceContext.getSharedPreferencesPath("foo").getParentFile() 374 .getCanonicalPath(); 375 final String cacheDir = ceContext.getCacheDir().getCanonicalPath(); 376 final String codeCacheDir = ceContext.getCodeCacheDir().getCanonicalPath(); 377 378 final Context deContext = createDeviceProtectedStorageContext(); 379 final String deviceRootDir = deContext.getDataDir().getCanonicalPath(); 380 final String deviceFilesDir = deContext.getFilesDir().getCanonicalPath(); 381 final String deviceNoBackupDir = deContext.getNoBackupFilesDir().getCanonicalPath(); 382 final String deviceDatabaseDir = deContext.getDatabasePath("foo").getParentFile() 383 .getCanonicalPath(); 384 final String deviceSharedPrefsDir = deContext.getSharedPreferencesPath("foo") 385 .getParentFile().getCanonicalPath(); 386 final String deviceCacheDir = deContext.getCacheDir().getCanonicalPath(); 387 final String deviceCodeCacheDir = deContext.getCodeCacheDir().getCanonicalPath(); 388 389 final String libDir = (appInfo.nativeLibraryDir != null) 390 ? new File(appInfo.nativeLibraryDir).getCanonicalPath() 391 : null; 392 393 // Maintain a set of excluded directories so that as we traverse the tree we know we're not 394 // going places we don't expect, and so the manifest includes can't take precedence over 395 // what the framework decides is not to be included. 396 final ArraySet<String> traversalExcludeSet = new ArraySet<String>(); 397 398 // Add the directories we always exclude. 399 traversalExcludeSet.add(filesDir); 400 traversalExcludeSet.add(noBackupDir); 401 traversalExcludeSet.add(databaseDir); 402 traversalExcludeSet.add(sharedPrefsDir); 403 traversalExcludeSet.add(cacheDir); 404 traversalExcludeSet.add(codeCacheDir); 405 406 traversalExcludeSet.add(deviceFilesDir); 407 traversalExcludeSet.add(deviceNoBackupDir); 408 traversalExcludeSet.add(deviceDatabaseDir); 409 traversalExcludeSet.add(deviceSharedPrefsDir); 410 traversalExcludeSet.add(deviceCacheDir); 411 traversalExcludeSet.add(deviceCodeCacheDir); 412 413 if (libDir != null) { 414 traversalExcludeSet.add(libDir); 415 } 416 417 // Root dir first. 418 applyXmlFiltersAndDoFullBackupForDomain( 419 packageName, FullBackup.ROOT_TREE_TOKEN, manifestIncludeMap, 420 manifestExcludeSet, traversalExcludeSet, data); 421 traversalExcludeSet.add(rootDir); 422 423 applyXmlFiltersAndDoFullBackupForDomain( 424 packageName, FullBackup.DEVICE_ROOT_TREE_TOKEN, manifestIncludeMap, 425 manifestExcludeSet, traversalExcludeSet, data); 426 traversalExcludeSet.add(deviceRootDir); 427 428 // Data dir next. 429 traversalExcludeSet.remove(filesDir); 430 applyXmlFiltersAndDoFullBackupForDomain( 431 packageName, FullBackup.FILES_TREE_TOKEN, manifestIncludeMap, 432 manifestExcludeSet, traversalExcludeSet, data); 433 traversalExcludeSet.add(filesDir); 434 435 traversalExcludeSet.remove(deviceFilesDir); 436 applyXmlFiltersAndDoFullBackupForDomain( 437 packageName, FullBackup.DEVICE_FILES_TREE_TOKEN, manifestIncludeMap, 438 manifestExcludeSet, traversalExcludeSet, data); 439 traversalExcludeSet.add(deviceFilesDir); 440 441 // Database directory. 442 traversalExcludeSet.remove(databaseDir); 443 applyXmlFiltersAndDoFullBackupForDomain( 444 packageName, FullBackup.DATABASE_TREE_TOKEN, manifestIncludeMap, 445 manifestExcludeSet, traversalExcludeSet, data); 446 traversalExcludeSet.add(databaseDir); 447 448 traversalExcludeSet.remove(deviceDatabaseDir); 449 applyXmlFiltersAndDoFullBackupForDomain( 450 packageName, FullBackup.DEVICE_DATABASE_TREE_TOKEN, manifestIncludeMap, 451 manifestExcludeSet, traversalExcludeSet, data); 452 traversalExcludeSet.add(deviceDatabaseDir); 453 454 // SharedPrefs. 455 traversalExcludeSet.remove(sharedPrefsDir); 456 applyXmlFiltersAndDoFullBackupForDomain( 457 packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, manifestIncludeMap, 458 manifestExcludeSet, traversalExcludeSet, data); 459 traversalExcludeSet.add(sharedPrefsDir); 460 461 traversalExcludeSet.remove(deviceSharedPrefsDir); 462 applyXmlFiltersAndDoFullBackupForDomain( 463 packageName, FullBackup.DEVICE_SHAREDPREFS_TREE_TOKEN, manifestIncludeMap, 464 manifestExcludeSet, traversalExcludeSet, data); 465 traversalExcludeSet.add(deviceSharedPrefsDir); 466 467 // getExternalFilesDir() location associated with this app. Technically there should 468 // not be any files here if the app does not properly have permission to access 469 // external storage, but edge cases happen. fullBackupFileTree() catches 470 // IOExceptions and similar, and treats them as non-fatal, so we rely on that; and 471 // we know a priori that processes running as the system UID are not permitted to 472 // access external storage, so we check for that as well to avoid nastygrams in 473 // the log. 474 if (Process.myUid() != Process.SYSTEM_UID) { 475 File efLocation = getExternalFilesDir(null); 476 if (efLocation != null) { 477 applyXmlFiltersAndDoFullBackupForDomain( 478 packageName, FullBackup.MANAGED_EXTERNAL_TREE_TOKEN, manifestIncludeMap, 479 manifestExcludeSet, traversalExcludeSet, data); 480 } 481 482 } 483 } 484 485 /** 486 * Notification that the application's current backup operation causes it to exceed 487 * the maximum size permitted by the transport. The ongoing backup operation is 488 * halted and rolled back: any data that had been stored by a previous backup operation 489 * is still intact. Typically the quota-exceeded state will be detected before any data 490 * is actually transmitted over the network. 491 * 492 * <p>The {@code quotaBytes} value is the total data size currently permitted for this 493 * application. If desired, the application can use this as a hint for determining 494 * how much data to store. For example, a messaging application might choose to 495 * store only the newest messages, dropping enough older content to stay under 496 * the quota. 497 * 498 * <p class="note">Note that the maximum quota for the application can change over 499 * time. In particular, in the future the quota may grow. Applications that adapt 500 * to the quota when deciding what data to store should be aware of this and implement 501 * their data storage mechanisms in a way that can take advantage of additional 502 * quota. 503 * 504 * @param backupDataBytes The amount of data measured while initializing the backup 505 * operation, if the total exceeds the app's alloted quota. If initial measurement 506 * suggested that the data would fit but then too much data was actually submitted 507 * as part of the operation, then this value is the amount of data that had been 508 * streamed into the transport at the time the quota was reached. 509 * @param quotaBytes The maximum data size that the transport currently permits 510 * this application to store as a backup. 511 */ 512 public void onQuotaExceeded(long backupDataBytes, long quotaBytes) { 513 } 514 515 /** 516 * Check whether the xml yielded any <include/> tag for the provided <code>domainToken</code>. 517 * If so, perform a {@link #fullBackupFileTree} which backs up the file or recurses if the path 518 * is a directory, but only if all the required flags of the include rule are satisfied by 519 * the transport. 520 */ 521 private void applyXmlFiltersAndDoFullBackupForDomain(String packageName, String domainToken, 522 Map<String, Set<PathWithRequiredFlags>> includeMap, 523 ArraySet<PathWithRequiredFlags> filterSet, ArraySet<String> traversalExcludeSet, 524 FullBackupDataOutput data) throws IOException { 525 if (includeMap == null || includeMap.size() == 0) { 526 // Do entire sub-tree for the provided token. 527 fullBackupFileTree(packageName, domainToken, 528 FullBackup.getBackupScheme(this).tokenToDirectoryPath(domainToken), 529 filterSet, traversalExcludeSet, data); 530 } else if (includeMap.get(domainToken) != null) { 531 // This will be null if the xml parsing didn't yield any rules for 532 // this domain (there may still be rules for other domains). 533 for (PathWithRequiredFlags includeFile : includeMap.get(domainToken)) { 534 if (areIncludeRequiredTransportFlagsSatisfied(includeFile.getRequiredFlags(), 535 data.getTransportFlags())) { 536 fullBackupFileTree(packageName, domainToken, includeFile.getPath(), filterSet, 537 traversalExcludeSet, data); 538 } 539 } 540 } 541 } 542 543 private boolean areIncludeRequiredTransportFlagsSatisfied(int includeFlags, 544 int transportFlags) { 545 // all bits that are set in includeFlags must also be set in transportFlags 546 return (transportFlags & includeFlags) == includeFlags; 547 } 548 549 /** 550 * Write an entire file as part of a full-backup operation. The file's contents 551 * will be delivered to the backup destination along with the metadata necessary 552 * to place it with the proper location and permissions on the device where the 553 * data is restored. 554 * 555 * <p class="note">Attempting to back up files in directories that are ignored by 556 * the backup system will have no effect. For example, if the app calls this method 557 * with a file inside the {@link #getNoBackupFilesDir()} directory, it will be ignored. 558 * See {@link #onFullBackup(FullBackupDataOutput)} for details on what directories 559 * are excluded from backups. 560 * 561 * @param file The file to be backed up. The file must exist and be readable by 562 * the caller. 563 * @param output The destination to which the backed-up file data will be sent. 564 */ 565 public final void fullBackupFile(File file, FullBackupDataOutput output) { 566 // Look up where all of our various well-defined dir trees live on this device 567 final String rootDir; 568 final String filesDir; 569 final String nbFilesDir; 570 final String dbDir; 571 final String spDir; 572 final String cacheDir; 573 final String codeCacheDir; 574 final String deviceRootDir; 575 final String deviceFilesDir; 576 final String deviceNbFilesDir; 577 final String deviceDbDir; 578 final String deviceSpDir; 579 final String deviceCacheDir; 580 final String deviceCodeCacheDir; 581 final String libDir; 582 583 String efDir = null; 584 String filePath; 585 586 ApplicationInfo appInfo = getApplicationInfo(); 587 588 try { 589 // System apps have control over where their default storage context 590 // is pointed, so we're always explicit when building paths. 591 final Context ceContext = createCredentialProtectedStorageContext(); 592 rootDir = ceContext.getDataDir().getCanonicalPath(); 593 filesDir = ceContext.getFilesDir().getCanonicalPath(); 594 nbFilesDir = ceContext.getNoBackupFilesDir().getCanonicalPath(); 595 dbDir = ceContext.getDatabasePath("foo").getParentFile().getCanonicalPath(); 596 spDir = ceContext.getSharedPreferencesPath("foo").getParentFile().getCanonicalPath(); 597 cacheDir = ceContext.getCacheDir().getCanonicalPath(); 598 codeCacheDir = ceContext.getCodeCacheDir().getCanonicalPath(); 599 600 final Context deContext = createDeviceProtectedStorageContext(); 601 deviceRootDir = deContext.getDataDir().getCanonicalPath(); 602 deviceFilesDir = deContext.getFilesDir().getCanonicalPath(); 603 deviceNbFilesDir = deContext.getNoBackupFilesDir().getCanonicalPath(); 604 deviceDbDir = deContext.getDatabasePath("foo").getParentFile().getCanonicalPath(); 605 deviceSpDir = deContext.getSharedPreferencesPath("foo").getParentFile() 606 .getCanonicalPath(); 607 deviceCacheDir = deContext.getCacheDir().getCanonicalPath(); 608 deviceCodeCacheDir = deContext.getCodeCacheDir().getCanonicalPath(); 609 610 libDir = (appInfo.nativeLibraryDir == null) 611 ? null 612 : new File(appInfo.nativeLibraryDir).getCanonicalPath(); 613 614 // may or may not have external files access to attempt backup/restore there 615 if (Process.myUid() != Process.SYSTEM_UID) { 616 File efLocation = getExternalFilesDir(null); 617 if (efLocation != null) { 618 efDir = efLocation.getCanonicalPath(); 619 } 620 } 621 622 // Now figure out which well-defined tree the file is placed in, working from 623 // most to least specific. We also specifically exclude the lib, cache, 624 // and code_cache dirs. 625 filePath = file.getCanonicalPath(); 626 } catch (IOException e) { 627 Log.w(TAG, "Unable to obtain canonical paths"); 628 return; 629 } 630 631 if (filePath.startsWith(cacheDir) 632 || filePath.startsWith(codeCacheDir) 633 || filePath.startsWith(nbFilesDir) 634 || filePath.startsWith(deviceCacheDir) 635 || filePath.startsWith(deviceCodeCacheDir) 636 || filePath.startsWith(deviceNbFilesDir) 637 || filePath.startsWith(libDir)) { 638 Log.w(TAG, "lib, cache, code_cache, and no_backup files are not backed up"); 639 return; 640 } 641 642 final String domain; 643 String rootpath = null; 644 if (filePath.startsWith(dbDir)) { 645 domain = FullBackup.DATABASE_TREE_TOKEN; 646 rootpath = dbDir; 647 } else if (filePath.startsWith(spDir)) { 648 domain = FullBackup.SHAREDPREFS_TREE_TOKEN; 649 rootpath = spDir; 650 } else if (filePath.startsWith(filesDir)) { 651 domain = FullBackup.FILES_TREE_TOKEN; 652 rootpath = filesDir; 653 } else if (filePath.startsWith(rootDir)) { 654 domain = FullBackup.ROOT_TREE_TOKEN; 655 rootpath = rootDir; 656 } else if (filePath.startsWith(deviceDbDir)) { 657 domain = FullBackup.DEVICE_DATABASE_TREE_TOKEN; 658 rootpath = deviceDbDir; 659 } else if (filePath.startsWith(deviceSpDir)) { 660 domain = FullBackup.DEVICE_SHAREDPREFS_TREE_TOKEN; 661 rootpath = deviceSpDir; 662 } else if (filePath.startsWith(deviceFilesDir)) { 663 domain = FullBackup.DEVICE_FILES_TREE_TOKEN; 664 rootpath = deviceFilesDir; 665 } else if (filePath.startsWith(deviceRootDir)) { 666 domain = FullBackup.DEVICE_ROOT_TREE_TOKEN; 667 rootpath = deviceRootDir; 668 } else if ((efDir != null) && filePath.startsWith(efDir)) { 669 domain = FullBackup.MANAGED_EXTERNAL_TREE_TOKEN; 670 rootpath = efDir; 671 } else { 672 Log.w(TAG, "File " + filePath + " is in an unsupported location; skipping"); 673 return; 674 } 675 676 // And now that we know where it lives, semantically, back it up appropriately 677 // In the measurement case, backupToTar() updates the size in output and returns 678 // without transmitting any file data. 679 if (DEBUG) Log.i(TAG, "backupFile() of " + filePath + " => domain=" + domain 680 + " rootpath=" + rootpath); 681 682 FullBackup.backupToTar(getPackageName(), domain, null, rootpath, filePath, output); 683 } 684 685 /** 686 * Scan the dir tree (if it actually exists) and process each entry we find. If the 687 * 'excludes' parameters are non-null, they are consulted each time a new file system entity 688 * is visited to see whether that entity (and its subtree, if appropriate) should be 689 * omitted from the backup process. 690 * 691 * @param systemExcludes An optional list of excludes. 692 * @hide 693 */ 694 protected final void fullBackupFileTree(String packageName, String domain, String startingPath, 695 ArraySet<PathWithRequiredFlags> manifestExcludes, 696 ArraySet<String> systemExcludes, 697 FullBackupDataOutput output) { 698 // Pull out the domain and set it aside to use when making the tarball. 699 String domainPath = FullBackup.getBackupScheme(this).tokenToDirectoryPath(domain); 700 if (domainPath == null) { 701 // Should never happen. 702 return; 703 } 704 705 File rootFile = new File(startingPath); 706 if (rootFile.exists()) { 707 LinkedList<File> scanQueue = new LinkedList<File>(); 708 scanQueue.add(rootFile); 709 710 while (scanQueue.size() > 0) { 711 File file = scanQueue.remove(0); 712 String filePath; 713 try { 714 // Ignore things that aren't "real" files or dirs 715 StructStat stat = Os.lstat(file.getPath()); 716 if (!OsConstants.S_ISREG(stat.st_mode) 717 && !OsConstants.S_ISDIR(stat.st_mode)) { 718 if (DEBUG) Log.i(TAG, "Not a file/dir (skipping)!: " + file); 719 continue; 720 } 721 722 // For all other verification, look at the canonicalized path 723 filePath = file.getCanonicalPath(); 724 725 // prune this subtree? 726 if (manifestExcludes != null 727 && manifestExcludesContainFilePath(manifestExcludes, filePath)) { 728 continue; 729 } 730 if (systemExcludes != null && systemExcludes.contains(filePath)) { 731 continue; 732 } 733 734 // If it's a directory, enqueue its contents for scanning. 735 if (OsConstants.S_ISDIR(stat.st_mode)) { 736 File[] contents = file.listFiles(); 737 if (contents != null) { 738 for (File entry : contents) { 739 scanQueue.add(0, entry); 740 } 741 } 742 } 743 } catch (IOException e) { 744 if (DEBUG) Log.w(TAG, "Error canonicalizing path of " + file); 745 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { 746 Log.v(FullBackup.TAG_XML_PARSER, "Error canonicalizing path of " + file); 747 } 748 continue; 749 } catch (ErrnoException e) { 750 if (DEBUG) Log.w(TAG, "Error scanning file " + file + " : " + e); 751 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { 752 Log.v(FullBackup.TAG_XML_PARSER, "Error scanning file " + file + " : " + e); 753 } 754 continue; 755 } 756 757 // Finally, back this file up (or measure it) before proceeding 758 FullBackup.backupToTar(packageName, domain, null, domainPath, filePath, output); 759 } 760 } 761 } 762 763 private boolean manifestExcludesContainFilePath( 764 ArraySet<PathWithRequiredFlags> manifestExcludes, String filePath) { 765 for (PathWithRequiredFlags exclude : manifestExcludes) { 766 String excludePath = exclude.getPath(); 767 if (excludePath != null && excludePath.equals(filePath)) { 768 return true; 769 } 770 } 771 return false; 772 } 773 774 /** 775 * Handle the data delivered via the given file descriptor during a full restore 776 * operation. The agent is given the path to the file's original location as well 777 * as its size and metadata. 778 * <p> 779 * The file descriptor can only be read for {@code size} bytes; attempting to read 780 * more data has undefined behavior. 781 * <p> 782 * The default implementation creates the destination file/directory and populates it 783 * with the data from the file descriptor, then sets the file's access mode and 784 * modification time to match the restore arguments. 785 * 786 * @param data A read-only file descriptor from which the agent can read {@code size} 787 * bytes of file data. 788 * @param size The number of bytes of file content to be restored to the given 789 * destination. If the file system object being restored is a directory, {@code size} 790 * will be zero. 791 * @param destination The File on disk to be restored with the given data. 792 * @param type The kind of file system object being restored. This will be either 793 * {@link BackupAgent#TYPE_FILE} or {@link BackupAgent#TYPE_DIRECTORY}. 794 * @param mode The access mode to be assigned to the destination after its data is 795 * written. This is in the standard format used by {@code chmod()}. 796 * @param mtime The modification time of the file when it was backed up, suitable to 797 * be assigned to the file after its data is written. 798 * @throws IOException 799 */ 800 public void onRestoreFile(ParcelFileDescriptor data, long size, 801 File destination, int type, long mode, long mtime) 802 throws IOException { 803 804 final boolean accept = isFileEligibleForRestore(destination); 805 // If we don't accept the file, consume the bytes from the pipe anyway. 806 FullBackup.restoreFile(data, size, type, mode, mtime, accept ? destination : null); 807 } 808 809 private boolean isFileEligibleForRestore(File destination) throws IOException { 810 FullBackup.BackupScheme bs = FullBackup.getBackupScheme(this); 811 if (!bs.isFullBackupContentEnabled()) { 812 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { 813 Log.v(FullBackup.TAG_XML_PARSER, 814 "onRestoreFile \"" + destination.getCanonicalPath() 815 + "\" : fullBackupContent not enabled for " + getPackageName()); 816 } 817 return false; 818 } 819 820 Map<String, Set<PathWithRequiredFlags>> includes = null; 821 ArraySet<PathWithRequiredFlags> excludes = null; 822 final String destinationCanonicalPath = destination.getCanonicalPath(); 823 try { 824 includes = bs.maybeParseAndGetCanonicalIncludePaths(); 825 excludes = bs.maybeParseAndGetCanonicalExcludePaths(); 826 } catch (XmlPullParserException e) { 827 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { 828 Log.v(FullBackup.TAG_XML_PARSER, 829 "onRestoreFile \"" + destinationCanonicalPath 830 + "\" : Exception trying to parse fullBackupContent xml file!" 831 + " Aborting onRestoreFile.", e); 832 } 833 return false; 834 } 835 836 if (excludes != null && 837 isFileSpecifiedInPathList(destination, excludes)) { 838 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { 839 Log.v(FullBackup.TAG_XML_PARSER, 840 "onRestoreFile: \"" + destinationCanonicalPath + "\": listed in" 841 + " excludes; skipping."); 842 } 843 return false; 844 } 845 846 if (includes != null && !includes.isEmpty()) { 847 // Rather than figure out the <include/> domain based on the path (a lot of code, and 848 // it's a small list), we'll go through and look for it. 849 boolean explicitlyIncluded = false; 850 for (Set<PathWithRequiredFlags> domainIncludes : includes.values()) { 851 explicitlyIncluded |= isFileSpecifiedInPathList(destination, domainIncludes); 852 if (explicitlyIncluded) { 853 break; 854 } 855 } 856 if (!explicitlyIncluded) { 857 if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { 858 Log.v(FullBackup.TAG_XML_PARSER, 859 "onRestoreFile: Trying to restore \"" 860 + destinationCanonicalPath + "\" but it isn't specified" 861 + " in the included files; skipping."); 862 } 863 return false; 864 } 865 } 866 return true; 867 } 868 869 /** 870 * @return True if the provided file is either directly in the provided list, or the provided 871 * file is within a directory in the list. 872 */ 873 private boolean isFileSpecifiedInPathList(File file, 874 Collection<PathWithRequiredFlags> canonicalPathList) throws IOException { 875 for (PathWithRequiredFlags canonical : canonicalPathList) { 876 String canonicalPath = canonical.getPath(); 877 File fileFromList = new File(canonicalPath); 878 if (fileFromList.isDirectory()) { 879 if (file.isDirectory()) { 880 // If they are both directories check exact equals. 881 return file.equals(fileFromList); 882 } else { 883 // O/w we have to check if the file is within the directory from the list. 884 return file.getCanonicalPath().startsWith(canonicalPath); 885 } 886 } else { 887 if (file.equals(fileFromList)) { 888 // Need to check the explicit "equals" so we don't end up with substrings. 889 return true; 890 } 891 } 892 } 893 return false; 894 } 895 896 /** 897 * Only specialized platform agents should overload this entry point to support 898 * restores to crazy non-app locations. 899 * @hide 900 */ 901 protected void onRestoreFile(ParcelFileDescriptor data, long size, 902 int type, String domain, String path, long mode, long mtime) 903 throws IOException { 904 String basePath = null; 905 906 if (DEBUG) Log.d(TAG, "onRestoreFile() size=" + size + " type=" + type 907 + " domain=" + domain + " relpath=" + path + " mode=" + mode 908 + " mtime=" + mtime); 909 910 basePath = FullBackup.getBackupScheme(this).tokenToDirectoryPath(domain); 911 if (domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) { 912 mode = -1; // < 0 is a token to skip attempting a chmod() 913 } 914 915 // Now that we've figured out where the data goes, send it on its way 916 if (basePath != null) { 917 // Canonicalize the nominal path and verify that it lies within the stated domain 918 File outFile = new File(basePath, path); 919 String outPath = outFile.getCanonicalPath(); 920 if (outPath.startsWith(basePath + File.separatorChar)) { 921 if (DEBUG) Log.i(TAG, "[" + domain + " : " + path + "] mapped to " + outPath); 922 onRestoreFile(data, size, outFile, type, mode, mtime); 923 return; 924 } else { 925 // Attempt to restore to a path outside the file's nominal domain. 926 if (DEBUG) { 927 Log.e(TAG, "Cross-domain restore attempt: " + outPath); 928 } 929 } 930 } 931 932 // Not a supported output location, or bad path: we need to consume the data 933 // anyway, so just use the default "copy the data out" implementation 934 // with a null destination. 935 if (DEBUG) Log.i(TAG, "[ skipping file " + path + "]"); 936 FullBackup.restoreFile(data, size, type, mode, mtime, null); 937 } 938 939 /** 940 * The application's restore operation has completed. This method is called after 941 * all available data has been delivered to the application for restore (via either 942 * the {@link #onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()} or 943 * {@link #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long) onRestoreFile()} 944 * callbacks). This provides the app with a stable end-of-restore opportunity to 945 * perform any appropriate post-processing on the data that was just delivered. 946 * 947 * @see #onRestore(BackupDataInput, int, ParcelFileDescriptor) 948 * @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long) 949 */ 950 public void onRestoreFinished() { 951 } 952 953 // ----- Core implementation ----- 954 955 /** @hide */ 956 public final IBinder onBind() { 957 return mBinder; 958 } 959 960 private final IBinder mBinder = new BackupServiceBinder().asBinder(); 961 962 /** @hide */ 963 public void attach(Context context) { 964 attachBaseContext(context); 965 } 966 967 // ----- IBackupService binder interface ----- 968 private class BackupServiceBinder extends IBackupAgent.Stub { 969 private static final String TAG = "BackupServiceBinder"; 970 971 @Override 972 public void doBackup(ParcelFileDescriptor oldState, 973 ParcelFileDescriptor data, 974 ParcelFileDescriptor newState, 975 long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags) 976 throws RemoteException { 977 // Ensure that we're running with the app's normal permission level 978 long ident = Binder.clearCallingIdentity(); 979 980 if (DEBUG) Log.v(TAG, "doBackup() invoked"); 981 BackupDataOutput output = new BackupDataOutput( 982 data.getFileDescriptor(), quotaBytes, transportFlags); 983 984 try { 985 BackupAgent.this.onBackup(oldState, output, newState); 986 } catch (IOException ex) { 987 Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); 988 throw new RuntimeException(ex); 989 } catch (RuntimeException ex) { 990 Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); 991 throw ex; 992 } finally { 993 // Ensure that any SharedPreferences writes have landed after the backup, 994 // in case the app code has side effects (since apps cannot provide this 995 // guarantee themselves). 996 waitForSharedPrefs(); 997 998 Binder.restoreCallingIdentity(ident); 999 try { 1000 callbackBinder.opComplete(token, 0); 1001 } catch (RemoteException e) { 1002 // we'll time out anyway, so we're safe 1003 } 1004 1005 // Don't close the fd out from under the system service if this was local 1006 if (Binder.getCallingPid() != Process.myPid()) { 1007 IoUtils.closeQuietly(oldState); 1008 IoUtils.closeQuietly(data); 1009 IoUtils.closeQuietly(newState); 1010 } 1011 } 1012 } 1013 1014 @Override 1015 public void doRestore(ParcelFileDescriptor data, long appVersionCode, 1016 ParcelFileDescriptor newState, 1017 int token, IBackupManager callbackBinder) throws RemoteException { 1018 // Ensure that we're running with the app's normal permission level 1019 long ident = Binder.clearCallingIdentity(); 1020 1021 if (DEBUG) Log.v(TAG, "doRestore() invoked"); 1022 1023 // Ensure that any side-effect SharedPreferences writes have landed *before* 1024 // we may be about to rewrite the file out from underneath 1025 waitForSharedPrefs(); 1026 1027 BackupDataInput input = new BackupDataInput(data.getFileDescriptor()); 1028 try { 1029 BackupAgent.this.onRestore(input, appVersionCode, newState); 1030 } catch (IOException ex) { 1031 Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex); 1032 throw new RuntimeException(ex); 1033 } catch (RuntimeException ex) { 1034 Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex); 1035 throw ex; 1036 } finally { 1037 // And bring live SharedPreferences instances up to date 1038 reloadSharedPreferences(); 1039 1040 Binder.restoreCallingIdentity(ident); 1041 try { 1042 callbackBinder.opComplete(token, 0); 1043 } catch (RemoteException e) { 1044 // we'll time out anyway, so we're safe 1045 } 1046 1047 if (Binder.getCallingPid() != Process.myPid()) { 1048 IoUtils.closeQuietly(data); 1049 IoUtils.closeQuietly(newState); 1050 } 1051 } 1052 } 1053 1054 @Override 1055 public void doFullBackup(ParcelFileDescriptor data, 1056 long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags) { 1057 // Ensure that we're running with the app's normal permission level 1058 long ident = Binder.clearCallingIdentity(); 1059 1060 if (DEBUG) Log.v(TAG, "doFullBackup() invoked"); 1061 1062 // Ensure that any SharedPreferences writes have landed *before* 1063 // we potentially try to back up the underlying files directly. 1064 waitForSharedPrefs(); 1065 1066 try { 1067 BackupAgent.this.onFullBackup(new FullBackupDataOutput( 1068 data, quotaBytes, transportFlags)); 1069 } catch (IOException ex) { 1070 Log.d(TAG, "onFullBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); 1071 throw new RuntimeException(ex); 1072 } catch (RuntimeException ex) { 1073 Log.d(TAG, "onFullBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); 1074 throw ex; 1075 } finally { 1076 // ... and then again after, as in the doBackup() case 1077 waitForSharedPrefs(); 1078 1079 // Send the EOD marker indicating that there is no more data 1080 // forthcoming from this agent. 1081 try { 1082 FileOutputStream out = new FileOutputStream(data.getFileDescriptor()); 1083 byte[] buf = new byte[4]; 1084 out.write(buf); 1085 } catch (IOException e) { 1086 Log.e(TAG, "Unable to finalize backup stream!"); 1087 } 1088 1089 Binder.restoreCallingIdentity(ident); 1090 try { 1091 callbackBinder.opComplete(token, 0); 1092 } catch (RemoteException e) { 1093 // we'll time out anyway, so we're safe 1094 } 1095 1096 if (Binder.getCallingPid() != Process.myPid()) { 1097 IoUtils.closeQuietly(data); 1098 } 1099 } 1100 } 1101 1102 public void doMeasureFullBackup(long quotaBytes, int token, IBackupManager callbackBinder, 1103 int transportFlags) { 1104 // Ensure that we're running with the app's normal permission level 1105 final long ident = Binder.clearCallingIdentity(); 1106 FullBackupDataOutput measureOutput = 1107 new FullBackupDataOutput(quotaBytes, transportFlags); 1108 1109 waitForSharedPrefs(); 1110 try { 1111 BackupAgent.this.onFullBackup(measureOutput); 1112 } catch (IOException ex) { 1113 Log.d(TAG, "onFullBackup[M] (" + BackupAgent.this.getClass().getName() + ") threw", ex); 1114 throw new RuntimeException(ex); 1115 } catch (RuntimeException ex) { 1116 Log.d(TAG, "onFullBackup[M] (" + BackupAgent.this.getClass().getName() + ") threw", ex); 1117 throw ex; 1118 } finally { 1119 Binder.restoreCallingIdentity(ident); 1120 try { 1121 callbackBinder.opComplete(token, measureOutput.getSize()); 1122 } catch (RemoteException e) { 1123 // timeout, so we're safe 1124 } 1125 } 1126 } 1127 1128 @Override 1129 public void doRestoreFile(ParcelFileDescriptor data, long size, 1130 int type, String domain, String path, long mode, long mtime, 1131 int token, IBackupManager callbackBinder) throws RemoteException { 1132 long ident = Binder.clearCallingIdentity(); 1133 try { 1134 BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime); 1135 } catch (IOException e) { 1136 Log.d(TAG, "onRestoreFile (" + BackupAgent.this.getClass().getName() + ") threw", e); 1137 throw new RuntimeException(e); 1138 } finally { 1139 // Ensure that any side-effect SharedPreferences writes have landed 1140 waitForSharedPrefs(); 1141 // And bring live SharedPreferences instances up to date 1142 reloadSharedPreferences(); 1143 1144 Binder.restoreCallingIdentity(ident); 1145 try { 1146 callbackBinder.opComplete(token, 0); 1147 } catch (RemoteException e) { 1148 // we'll time out anyway, so we're safe 1149 } 1150 1151 if (Binder.getCallingPid() != Process.myPid()) { 1152 IoUtils.closeQuietly(data); 1153 } 1154 } 1155 } 1156 1157 @Override 1158 public void doRestoreFinished(int token, IBackupManager callbackBinder) { 1159 long ident = Binder.clearCallingIdentity(); 1160 try { 1161 BackupAgent.this.onRestoreFinished(); 1162 } catch (Exception e) { 1163 Log.d(TAG, "onRestoreFinished (" + BackupAgent.this.getClass().getName() + ") threw", e); 1164 throw e; 1165 } finally { 1166 // Ensure that any side-effect SharedPreferences writes have landed 1167 waitForSharedPrefs(); 1168 1169 Binder.restoreCallingIdentity(ident); 1170 try { 1171 callbackBinder.opComplete(token, 0); 1172 } catch (RemoteException e) { 1173 // we'll time out anyway, so we're safe 1174 } 1175 } 1176 } 1177 1178 @Override 1179 public void fail(String message) { 1180 getHandler().post(new FailRunnable(message)); 1181 } 1182 1183 @Override 1184 public void doQuotaExceeded(long backupDataBytes, long quotaBytes) { 1185 long ident = Binder.clearCallingIdentity(); 1186 try { 1187 BackupAgent.this.onQuotaExceeded(backupDataBytes, quotaBytes); 1188 } catch (Exception e) { 1189 Log.d(TAG, "onQuotaExceeded(" + BackupAgent.this.getClass().getName() + ") threw", 1190 e); 1191 throw e; 1192 } finally { 1193 waitForSharedPrefs(); 1194 Binder.restoreCallingIdentity(ident); 1195 } 1196 } 1197 } 1198 1199 static class FailRunnable implements Runnable { 1200 private String mMessage; 1201 1202 FailRunnable(String message) { 1203 mMessage = message; 1204 } 1205 1206 @Override 1207 public void run() { 1208 throw new IllegalStateException(mMessage); 1209 } 1210 } 1211 } 1212