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