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 getHandler()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 run()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 waitForSharedPrefs()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 BackupAgent()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 */ onCreate()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 */ onDestroy()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 */ onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)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 */ onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)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 */ onRestore(BackupDataInput data, long appVersionCode, ParcelFileDescriptor newState)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 */ onFullBackup(FullBackupDataOutput data)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 */ onQuotaExceeded(long backupDataBytes, long quotaBytes)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 */ applyXmlFiltersAndDoFullBackupForDomain(String packageName, String domainToken, Map<String, Set<PathWithRequiredFlags>> includeMap, ArraySet<PathWithRequiredFlags> filterSet, ArraySet<String> traversalExcludeSet, FullBackupDataOutput data)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 areIncludeRequiredTransportFlagsSatisfied(int includeFlags, int transportFlags)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 */ fullBackupFile(File file, FullBackupDataOutput output)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 */ fullBackupFileTree(String packageName, String domain, String startingPath, ArraySet<PathWithRequiredFlags> manifestExcludes, ArraySet<String> systemExcludes, FullBackupDataOutput output)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 manifestExcludesContainFilePath( ArraySet<PathWithRequiredFlags> manifestExcludes, String filePath)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 */ onRestoreFile(ParcelFileDescriptor data, long size, File destination, int type, long mode, long mtime)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 isFileEligibleForRestore(File destination)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 */ isFileSpecifiedInPathList(File file, Collection<PathWithRequiredFlags> canonicalPathList)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 */ onRestoreFile(ParcelFileDescriptor data, long size, int type, String domain, String path, long mode, long mtime)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 */ onRestoreFinished()950 public void onRestoreFinished() { 951 } 952 953 // ----- Core implementation ----- 954 955 /** @hide */ onBind()956 public final IBinder onBind() { 957 return mBinder; 958 } 959 960 private final IBinder mBinder = new BackupServiceBinder().asBinder(); 961 962 /** @hide */ attach(Context context)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 doBackup(ParcelFileDescriptor oldState, ParcelFileDescriptor data, ParcelFileDescriptor newState, long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags)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 doRestore(ParcelFileDescriptor data, long appVersionCode, ParcelFileDescriptor newState, int token, IBackupManager callbackBinder)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 doFullBackup(ParcelFileDescriptor data, long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags)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 doMeasureFullBackup(long quotaBytes, int token, IBackupManager callbackBinder, int transportFlags)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 doRestoreFile(ParcelFileDescriptor data, long size, int type, String domain, String path, long mode, long mtime, int token, IBackupManager callbackBinder)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 doRestoreFinished(int token, IBackupManager callbackBinder)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 fail(String message)1179 public void fail(String message) { 1180 getHandler().post(new FailRunnable(message)); 1181 } 1182 1183 @Override doQuotaExceeded(long backupDataBytes, long quotaBytes)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 FailRunnable(String message)1202 FailRunnable(String message) { 1203 mMessage = message; 1204 } 1205 1206 @Override run()1207 public void run() { 1208 throw new IllegalStateException(mMessage); 1209 } 1210 } 1211 } 1212