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 com.android.server.backup; 18 19 import android.app.backup.BackupAgent; 20 import android.app.backup.BackupDataInput; 21 import android.app.backup.BackupDataOutput; 22 import android.content.ComponentName; 23 import android.content.pm.ApplicationInfo; 24 import android.content.pm.PackageInfo; 25 import android.content.pm.PackageManager; 26 import android.content.pm.PackageManager.NameNotFoundException; 27 import android.content.pm.ResolveInfo; 28 import android.content.pm.Signature; 29 import android.content.pm.SigningInfo; 30 import android.os.Build; 31 import android.os.ParcelFileDescriptor; 32 import android.util.Slog; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.server.backup.utils.BackupEligibilityRules; 36 37 import java.io.BufferedInputStream; 38 import java.io.BufferedOutputStream; 39 import java.io.ByteArrayInputStream; 40 import java.io.ByteArrayOutputStream; 41 import java.io.DataInputStream; 42 import java.io.DataOutputStream; 43 import java.io.EOFException; 44 import java.io.FileInputStream; 45 import java.io.FileOutputStream; 46 import java.io.IOException; 47 import java.util.ArrayList; 48 import java.util.HashMap; 49 import java.util.HashSet; 50 import java.util.List; 51 import java.util.Set; 52 53 /** 54 * We back up the signatures of each package so that during a system restore, we can verify that the 55 * app whose data we think we have matches the app actually resident on the device. 56 * 57 * <p>Since the Package Manager isn't a proper "application" we just provide a direct IBackupAgent 58 * implementation and hand-construct it at need. 59 */ 60 public class PackageManagerBackupAgent extends BackupAgent { 61 private static final String TAG = "PMBA"; 62 private static final boolean DEBUG = false; 63 64 // key under which we store global metadata (individual app metadata 65 // is stored using the package name as a key) 66 @VisibleForTesting static final String GLOBAL_METADATA_KEY = "@meta@"; 67 68 // key under which we store the identity of the user's chosen default home app 69 private static final String DEFAULT_HOME_KEY = "@home@"; 70 71 // Sentinel: start of state file, followed by a version number 72 // Note that STATE_FILE_VERSION=2 is tied to UNDEFINED_ANCESTRAL_RECORD_VERSION=-1 *as well as* 73 // ANCESTRAL_RECORD_VERSION=1 (introduced Android P). 74 // Should the ANCESTRAL_RECORD_VERSION be bumped up in the future, STATE_FILE_VERSION will also 75 // need bumping up, assuming more data needs saving to the state file. 76 @VisibleForTesting static final String STATE_FILE_HEADER = "=state="; 77 @VisibleForTesting static final int STATE_FILE_VERSION = 2; 78 79 // key under which we store the saved ancestral-dataset format (starting from Android P) 80 // IMPORTANT: this key needs to come first in the restore data stream (to find out 81 // whether this version of Android knows how to restore the incoming data set), so it needs 82 // to be always the first one in alphabetical order of all the keys 83 @VisibleForTesting static final String ANCESTRAL_RECORD_KEY = "@ancestral_record@"; 84 85 // Current version of the saved ancestral-dataset format 86 // Note that this constant was not used until Android P, and started being used 87 // to version @pm@ data for forwards-compatibility. 88 @VisibleForTesting static final int ANCESTRAL_RECORD_VERSION = 1; 89 90 // Undefined version of the saved ancestral-dataset file format means that the restore data 91 // is coming from pre-Android P device. 92 private static final int UNDEFINED_ANCESTRAL_RECORD_VERSION = -1; 93 94 private int mUserId; 95 private List<PackageInfo> mAllPackages; 96 private PackageManager mPackageManager; 97 // version & signature info of each app in a restore set 98 private HashMap<String, Metadata> mRestoredSignatures; 99 // The version info of each backed-up app as read from the state file 100 private HashMap<String, Metadata> mStateVersions = new HashMap<String, Metadata>(); 101 102 // The ancestral record version as read from the state file 103 private int mStoredAncestralRecordVersion; 104 105 private final HashSet<String> mExisting = new HashSet<String>(); 106 private int mStoredSdkVersion; 107 private String mStoredIncrementalVersion; 108 private ComponentName mStoredHomeComponent; 109 private long mStoredHomeVersion; 110 private ArrayList<byte[]> mStoredHomeSigHashes; 111 112 private boolean mHasMetadata; 113 private ComponentName mRestoredHome; 114 private long mRestoredHomeVersion; 115 private String mRestoredHomeInstaller; 116 private ArrayList<byte[]> mRestoredHomeSigHashes; 117 118 // For compactness we store the SHA-256 hash of each app's Signatures 119 // rather than the Signature blocks themselves. 120 public class Metadata { 121 public long versionCode; 122 public ArrayList<byte[]> sigHashes; 123 Metadata(long version, ArrayList<byte[]> hashes)124 Metadata(long version, ArrayList<byte[]> hashes) { 125 versionCode = version; 126 sigHashes = hashes; 127 } 128 } 129 130 // We're constructed with the set of applications that are participating 131 // in backup. This set changes as apps are installed & removed. PackageManagerBackupAgent( PackageManager packageMgr, List<PackageInfo> packages, int userId)132 public PackageManagerBackupAgent( 133 PackageManager packageMgr, List<PackageInfo> packages, int userId) { 134 init(packageMgr, packages, userId); 135 } 136 PackageManagerBackupAgent( PackageManager packageMgr, int userId, BackupEligibilityRules backupEligibilityRules)137 public PackageManagerBackupAgent( 138 PackageManager packageMgr, int userId, BackupEligibilityRules backupEligibilityRules) { 139 init(packageMgr, null, userId); 140 141 evaluateStorablePackages(backupEligibilityRules); 142 } 143 init(PackageManager packageMgr, List<PackageInfo> packages, int userId)144 private void init(PackageManager packageMgr, List<PackageInfo> packages, int userId) { 145 mPackageManager = packageMgr; 146 mAllPackages = packages; 147 mRestoredSignatures = null; 148 mHasMetadata = false; 149 150 mStoredSdkVersion = Build.VERSION.SDK_INT; 151 mStoredIncrementalVersion = Build.VERSION.INCREMENTAL; 152 mUserId = userId; 153 } 154 155 // We will need to refresh our understanding of what is eligible for 156 // backup periodically; this entry point serves that purpose. evaluateStorablePackages(BackupEligibilityRules backupEligibilityRules)157 public void evaluateStorablePackages(BackupEligibilityRules backupEligibilityRules) { 158 mAllPackages = getStorableApplications(mPackageManager, mUserId, backupEligibilityRules); 159 } 160 161 /** Gets all packages installed on user {@code userId} eligible for backup. */ getStorableApplications( PackageManager pm, int userId, BackupEligibilityRules backupEligibilityRules)162 public static List<PackageInfo> getStorableApplications( 163 PackageManager pm, int userId, BackupEligibilityRules backupEligibilityRules) { 164 List<PackageInfo> pkgs = 165 pm.getInstalledPackagesAsUser(PackageManager.GET_SIGNING_CERTIFICATES, userId); 166 int N = pkgs.size(); 167 for (int a = N - 1; a >= 0; a--) { 168 PackageInfo pkg = pkgs.get(a); 169 if (!backupEligibilityRules.appIsEligibleForBackup(pkg.applicationInfo)) { 170 pkgs.remove(a); 171 } 172 } 173 return pkgs; 174 } 175 hasMetadata()176 public boolean hasMetadata() { 177 return mHasMetadata; 178 } 179 getSourceSdk()180 public int getSourceSdk() { 181 return mStoredSdkVersion; 182 } 183 getRestoredMetadata(String packageName)184 public Metadata getRestoredMetadata(String packageName) { 185 if (mRestoredSignatures == null) { 186 Slog.w(TAG, "getRestoredMetadata() before metadata read!"); 187 return null; 188 } 189 190 return mRestoredSignatures.get(packageName); 191 } 192 getRestoredPackages()193 public Set<String> getRestoredPackages() { 194 if (mRestoredSignatures == null) { 195 Slog.w(TAG, "getRestoredPackages() before metadata read!"); 196 return null; 197 } 198 199 // This is technically the set of packages on the originating handset 200 // that had backup agents at all, not limited to the set of packages 201 // that had actually contributed a restore dataset, but it's a 202 // close enough approximation for our purposes and does not require any 203 // additional involvement by the transport to obtain. 204 return mRestoredSignatures.keySet(); 205 } 206 207 @Override onBackup( ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState)208 public void onBackup( 209 ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) { 210 if (DEBUG) Slog.v(TAG, "onBackup()"); 211 212 // The backed up data is the signature block for each app, keyed by the package name. 213 214 ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream(); // we'll reuse these 215 DataOutputStream outputBufferStream = new DataOutputStream(outputBuffer); 216 parseStateFile(oldState); 217 218 // If the stored version string differs, we need to re-backup all 219 // of the metadata. We force this by removing everything from the 220 // "already backed up" map built by parseStateFile(). 221 if (mStoredIncrementalVersion == null 222 || !mStoredIncrementalVersion.equals(Build.VERSION.INCREMENTAL)) { 223 Slog.i( 224 TAG, 225 "Previous metadata " 226 + mStoredIncrementalVersion 227 + " mismatch vs " 228 + Build.VERSION.INCREMENTAL 229 + " - rewriting"); 230 mExisting.clear(); 231 } 232 233 /* 234 * Ancestral record version: 235 * 236 * int ancestralRecordVersion -- the version of the format in which this backup set is 237 * produced 238 */ 239 boolean upgradingAncestralRecordVersion = false; 240 try { 241 if (!mExisting.contains(ANCESTRAL_RECORD_KEY)) { 242 // The old state does not store info on ancestral record 243 Slog.v( 244 TAG, 245 "No ancestral record version in the old state. Storing " 246 + "ancestral record version key"); 247 outputBufferStream.writeInt(ANCESTRAL_RECORD_VERSION); 248 writeEntity(data, ANCESTRAL_RECORD_KEY, outputBuffer.toByteArray()); 249 upgradingAncestralRecordVersion = true; 250 } else if (mStoredAncestralRecordVersion != ANCESTRAL_RECORD_VERSION) { 251 // The current ancestral record version has changed from the old state 252 Slog.v( 253 TAG, 254 "Ancestral record version has changed from old state. Storing" 255 + "ancestral record version key"); 256 outputBufferStream.writeInt(ANCESTRAL_RECORD_VERSION); 257 writeEntity(data, ANCESTRAL_RECORD_KEY, outputBuffer.toByteArray()); 258 upgradingAncestralRecordVersion = true; 259 mExisting.remove(ANCESTRAL_RECORD_KEY); 260 } else { 261 if (DEBUG) Slog.v(TAG, "Ancestral record version has not changed"); 262 mExisting.remove(ANCESTRAL_RECORD_KEY); 263 } 264 265 /* 266 * Global metadata: 267 * 268 * int SDKversion -- the SDK version of the OS itself on the device 269 * that produced this backup set. Before Android P it was used to 270 * reject backups from later OSes onto earlier ones. 271 * String incremental -- the incremental release name of the OS stored in 272 * the backup set. 273 */ 274 outputBuffer.reset(); 275 if (!mExisting.contains(GLOBAL_METADATA_KEY)) { 276 if (DEBUG) Slog.v(TAG, "Storing global metadata key"); 277 outputBufferStream.writeInt(Build.VERSION.SDK_INT); 278 outputBufferStream.writeUTF(Build.VERSION.INCREMENTAL); 279 writeEntity(data, GLOBAL_METADATA_KEY, outputBuffer.toByteArray()); 280 } else { 281 if (DEBUG) Slog.v(TAG, "Global metadata key already stored"); 282 // don't consider it to have been skipped/deleted 283 mExisting.remove(GLOBAL_METADATA_KEY); 284 } 285 286 // For each app we have on device, see if we've backed it up yet. If not, 287 // write its signature block to the output, keyed on the package name. 288 for (PackageInfo pkg : mAllPackages) { 289 String packName = pkg.packageName; 290 if (packName.equals(GLOBAL_METADATA_KEY)) { 291 // We've already handled the metadata key; skip it here 292 continue; 293 } else { 294 PackageInfo info = null; 295 try { 296 info = 297 mPackageManager.getPackageInfoAsUser( 298 packName, PackageManager.GET_SIGNING_CERTIFICATES, mUserId); 299 } catch (NameNotFoundException e) { 300 // Weird; we just found it, and now are told it doesn't exist. 301 // Treat it as having been removed from the device. 302 mExisting.add(packName); 303 continue; 304 } 305 306 if (mExisting.contains(packName)) { 307 // We have backed up this app before. If the current ancestral record 308 // version is the same as what is in the old state, check whether the 309 // version of the backup matches the version of the current app; if they 310 // don't match, the app has been updated and we need to store its 311 // metadata again. In either case, take it out of mExisting so that 312 // we don't consider it deleted later. 313 mExisting.remove(packName); 314 if (!upgradingAncestralRecordVersion 315 && info.getLongVersionCode() 316 == mStateVersions.get(packName).versionCode) { 317 continue; 318 } 319 } 320 321 SigningInfo signingInfo = info.signingInfo; 322 if (signingInfo == null) { 323 Slog.w( 324 TAG, 325 "Not backing up package " 326 + packName 327 + " since it appears to have no signatures."); 328 continue; 329 } 330 331 // We need to store this app's metadata 332 /* 333 * Metadata for each package: 334 * 335 * int version -- [4] the package's versionCode 336 * byte[] signatures -- [len] flattened signature hash array of the package 337 */ 338 339 // marshal the version code in a canonical form 340 outputBuffer.reset(); 341 if (info.versionCodeMajor != 0) { 342 outputBufferStream.writeInt(Integer.MIN_VALUE); 343 outputBufferStream.writeLong(info.getLongVersionCode()); 344 } else { 345 outputBufferStream.writeInt(info.versionCode); 346 } 347 // retrieve the newest sigs to back up 348 Signature[] infoSignatures = signingInfo.getApkContentsSigners(); 349 writeSignatureHashArray( 350 outputBufferStream, BackupUtils.hashSignatureArray(infoSignatures)); 351 352 if (DEBUG) { 353 Slog.v( 354 TAG, 355 "+ writing metadata for " 356 + packName 357 + " version=" 358 + info.getLongVersionCode() 359 + " entityLen=" 360 + outputBuffer.size()); 361 } 362 363 // Now we can write the backup entity for this package 364 writeEntity(data, packName, outputBuffer.toByteArray()); 365 } 366 } 367 368 // At this point, the only entries in 'existing' are apps that were 369 // mentioned in the saved state file, but appear to no longer be present 370 // on the device. 371 if (!mExisting.isEmpty()) { 372 // If the ancestral record version has changed from the previous state we delete the 373 // existing keys for apps that are no longer installed. We should do this, otherwise 374 // we'd leave a key/value pair behind in the old format which could cause problems. 375 if (upgradingAncestralRecordVersion) { 376 for (String pkgName : mExisting) { 377 Slog.i( 378 TAG, 379 "Ancestral state updated - Deleting uninstalled package: " 380 + pkgName 381 + " from existing backup"); 382 data.writeEntityHeader(pkgName, -1); 383 } 384 mExisting.clear(); 385 } else { 386 // If the ancestral record version is unchanged from the previous state, we 387 // don't to anything to preserve the key/value entry for them. We do this 388 // because we want the right thing to happen if the user goes through a 389 // backup / uninstall / backup / reinstall sequence. 390 if (DEBUG) { 391 StringBuilder sb = new StringBuilder(64); 392 sb.append("Preserving metadata for deleted packages:"); 393 for (String app : mExisting) { 394 sb.append(' '); 395 sb.append(app); 396 } 397 Slog.v(TAG, sb.toString()); 398 } 399 } 400 } 401 } catch (IOException e) { 402 // Real error writing data 403 Slog.e(TAG, "Unable to write package backup data file!"); 404 return; 405 } 406 407 // Finally, write the new state blob -- just the list of all apps we handled 408 writeStateFile(mAllPackages, newState); 409 } 410 writeEntity(BackupDataOutput data, String key, byte[] bytes)411 private static void writeEntity(BackupDataOutput data, String key, byte[] bytes) 412 throws IOException { 413 data.writeEntityHeader(key, bytes.length); 414 data.writeEntityData(bytes, bytes.length); 415 } 416 417 @Override onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)418 public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) 419 throws IOException { 420 if (DEBUG) Slog.v(TAG, "onRestore()"); 421 422 // "Restore" here is a misnomer. What we're really doing is reading back the 423 // set of app signatures associated with each backed-up app in this restore 424 // image. We'll use those later to determine what we can legitimately restore. 425 426 // we expect the ANCESTRAL_RECORD_KEY ("@ancestral_record@") to always come first in the 427 // restore set - based on that value we use different mechanisms to consume the data; 428 // if the ANCESTRAL_RECORD_KEY is missing in the restore set, it means that the data is 429 // is coming from a pre-Android P device, and we consume the header data in the legacy way 430 // TODO: add a CTS test to verify that backups of PMBA generated on Android P+ always 431 // contain the ANCESTRAL_RECORD_KEY, and it's always the first key 432 int ancestralRecordVersion = getAncestralRecordVersionValue(data); 433 434 RestoreDataConsumer consumer = getRestoreDataConsumer(ancestralRecordVersion); 435 if (consumer == null) { 436 Slog.w( 437 TAG, 438 "Ancestral restore set version is unknown" 439 + " to this Android version; not restoring"); 440 return; 441 } else { 442 consumer.consumeRestoreData(data); 443 } 444 } 445 getAncestralRecordVersionValue(BackupDataInput data)446 private int getAncestralRecordVersionValue(BackupDataInput data) throws IOException { 447 int ancestralRecordVersionValue = UNDEFINED_ANCESTRAL_RECORD_VERSION; 448 if (data.readNextHeader()) { 449 String key = data.getKey(); 450 int dataSize = data.getDataSize(); 451 452 if (DEBUG) Slog.v(TAG, " got key=" + key + " dataSize=" + dataSize); 453 454 if (ANCESTRAL_RECORD_KEY.equals(key)) { 455 // generic setup to parse any entity data 456 byte[] inputBytes = new byte[dataSize]; 457 data.readEntityData(inputBytes, 0, dataSize); 458 ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes); 459 DataInputStream inputBufferStream = new DataInputStream(inputBuffer); 460 461 ancestralRecordVersionValue = inputBufferStream.readInt(); 462 } 463 } 464 return ancestralRecordVersionValue; 465 } 466 getRestoreDataConsumer(int ancestralRecordVersion)467 private RestoreDataConsumer getRestoreDataConsumer(int ancestralRecordVersion) { 468 switch (ancestralRecordVersion) { 469 case UNDEFINED_ANCESTRAL_RECORD_VERSION: 470 return new LegacyRestoreDataConsumer(); 471 case 1: 472 return new AncestralVersion1RestoreDataConsumer(); 473 default: 474 Slog.e(TAG, "Unrecognized ANCESTRAL_RECORD_VERSION: " + ancestralRecordVersion); 475 return null; 476 } 477 } 478 writeSignatureHashArray(DataOutputStream out, ArrayList<byte[]> hashes)479 private static void writeSignatureHashArray(DataOutputStream out, ArrayList<byte[]> hashes) 480 throws IOException { 481 // the number of entries in the array 482 out.writeInt(hashes.size()); 483 484 // the hash arrays themselves as length + contents 485 for (byte[] buffer : hashes) { 486 out.writeInt(buffer.length); 487 out.write(buffer); 488 } 489 } 490 readSignatureHashArray(DataInputStream in)491 private static ArrayList<byte[]> readSignatureHashArray(DataInputStream in) { 492 try { 493 int num; 494 try { 495 num = in.readInt(); 496 } catch (EOFException e) { 497 // clean termination 498 Slog.w(TAG, "Read empty signature block"); 499 return null; 500 } 501 502 if (DEBUG) Slog.v(TAG, " ... unflatten read " + num); 503 504 // Sensical? 505 if (num > 20) { 506 Slog.e(TAG, "Suspiciously large sig count in restore data; aborting"); 507 throw new IllegalStateException("Bad restore state"); 508 } 509 510 // This could be a "legacy" block of actual signatures rather than their hashes. 511 // If this is the case, convert them now. We judge based on the payload size: 512 // if the blocks are all 256 bits (32 bytes) then we take them to be SHA-256 hashes; 513 // otherwise we take them to be Signatures. 514 boolean nonHashFound = false; 515 ArrayList<byte[]> sigs = new ArrayList<byte[]>(num); 516 for (int i = 0; i < num; i++) { 517 int len = in.readInt(); 518 byte[] readHash = new byte[len]; 519 in.read(readHash); 520 sigs.add(readHash); 521 if (len != 32) { 522 nonHashFound = true; 523 } 524 } 525 526 if (nonHashFound) { 527 // Replace with the hashes. 528 sigs = BackupUtils.hashSignatureArray(sigs); 529 } 530 531 return sigs; 532 } catch (IOException e) { 533 Slog.e(TAG, "Unable to read signatures"); 534 return null; 535 } 536 } 537 538 // Util: parse out an existing state file into a usable structure parseStateFile(ParcelFileDescriptor stateFile)539 private void parseStateFile(ParcelFileDescriptor stateFile) { 540 mExisting.clear(); 541 mStateVersions.clear(); 542 mStoredSdkVersion = 0; 543 mStoredIncrementalVersion = null; 544 mStoredHomeComponent = null; 545 mStoredHomeVersion = 0; 546 mStoredHomeSigHashes = null; 547 mStoredAncestralRecordVersion = UNDEFINED_ANCESTRAL_RECORD_VERSION; 548 549 // The state file is just the list of app names we have stored signatures for 550 // with the exception of the metadata block, to which is also appended the 551 // version numbers corresponding with the last time we wrote this PM block. 552 // If they mismatch the current system, we'll re-store the metadata key. 553 FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor()); 554 BufferedInputStream inbuffer = new BufferedInputStream(instream); 555 DataInputStream in = new DataInputStream(inbuffer); 556 557 try { 558 boolean ignoreExisting = false; 559 String pkg = in.readUTF(); 560 561 // Validate the state file version is sensical to us 562 if (pkg.equals(STATE_FILE_HEADER)) { 563 int stateVersion = in.readInt(); 564 if (stateVersion > STATE_FILE_VERSION) { 565 Slog.w( 566 TAG, 567 "Unsupported state file version " 568 + stateVersion 569 + ", redoing from start"); 570 return; 571 } 572 pkg = in.readUTF(); 573 } else { 574 // This is an older version of the state file in which the lead element 575 // is not a STATE_FILE_VERSION string. If that's the case, we want to 576 // make sure to write our full backup dataset when given an opportunity. 577 // We trigger that by simply not marking the restored package metadata 578 // as known-to-exist-in-archive. 579 Slog.i(TAG, "Older version of saved state - rewriting"); 580 ignoreExisting = true; 581 } 582 583 // First comes the ancestral record block headed by the ANCESTRAL_RECORD_KEY tag 584 if (pkg.equals(ANCESTRAL_RECORD_KEY)) { 585 mStoredAncestralRecordVersion = in.readInt(); 586 if (!ignoreExisting) { 587 mExisting.add(ANCESTRAL_RECORD_KEY); 588 } 589 pkg = in.readUTF(); // set up for the next block of state 590 } else { 591 // This is an old version of the state file in which ANCESTRAL_RECORD_KEY is not 592 // stored. In this case onBackup will write the ANCESTRAL_KEY_VALUE to the new 593 // state. 594 Slog.i( 595 TAG, 596 "Older version of saved state - does not contain ancestral record " 597 + "version"); 598 } 599 600 // Then comes the preferred home app data, if any, headed by the DEFAULT_HOME_KEY tag 601 // Note that Default home app data is no longer backed up by this agent. 602 if (pkg.equals(DEFAULT_HOME_KEY)) { 603 // flattened component name, version, signature of the home app 604 mStoredHomeComponent = ComponentName.unflattenFromString(in.readUTF()); 605 mStoredHomeVersion = in.readLong(); 606 mStoredHomeSigHashes = readSignatureHashArray(in); 607 608 pkg = in.readUTF(); // set up for the next block of state 609 } else { 610 // else no preferred home app on the ancestral device - fall through to the rest 611 } 612 613 // After (possible) home app data comes the global metadata block 614 if (pkg.equals(GLOBAL_METADATA_KEY)) { 615 mStoredSdkVersion = in.readInt(); 616 mStoredIncrementalVersion = in.readUTF(); 617 if (!ignoreExisting) { 618 mExisting.add(GLOBAL_METADATA_KEY); 619 } 620 } else { 621 Slog.e(TAG, "No global metadata in state file!"); 622 return; 623 } 624 625 // The global metadata was last; now read all the apps 626 while (true) { 627 pkg = in.readUTF(); 628 int versionCodeInt = in.readInt(); 629 long versionCode; 630 if (versionCodeInt == Integer.MIN_VALUE) { 631 versionCode = in.readLong(); 632 } else { 633 versionCode = versionCodeInt; 634 } 635 636 if (!ignoreExisting) { 637 mExisting.add(pkg); 638 } 639 mStateVersions.put(pkg, new Metadata(versionCode, null)); 640 } 641 } catch (EOFException eof) { 642 // safe; we're done 643 } catch (IOException e) { 644 // whoops, bad state file. abort. 645 Slog.e(TAG, "Unable to read Package Manager state file: " + e); 646 } 647 } 648 getPreferredHomeComponent()649 private ComponentName getPreferredHomeComponent() { 650 return mPackageManager.getHomeActivities(new ArrayList<ResolveInfo>()); 651 } 652 653 // Util: write out our new backup state file 654 @VisibleForTesting writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile)655 static void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) { 656 FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); 657 BufferedOutputStream outbuf = new BufferedOutputStream(outstream); 658 DataOutputStream out = new DataOutputStream(outbuf); 659 660 // by the time we get here we know we've done all our backing up 661 try { 662 // state file version header 663 out.writeUTF(STATE_FILE_HEADER); 664 out.writeInt(STATE_FILE_VERSION); 665 666 // Record the ancestral record 667 out.writeUTF(ANCESTRAL_RECORD_KEY); 668 out.writeInt(ANCESTRAL_RECORD_VERSION); 669 670 // Conclude with the metadata block 671 out.writeUTF(GLOBAL_METADATA_KEY); 672 out.writeInt(Build.VERSION.SDK_INT); 673 out.writeUTF(Build.VERSION.INCREMENTAL); 674 675 // now write all the app names + versions 676 for (PackageInfo pkg : pkgs) { 677 out.writeUTF(pkg.packageName); 678 if (pkg.versionCodeMajor != 0) { 679 out.writeInt(Integer.MIN_VALUE); 680 out.writeLong(pkg.getLongVersionCode()); 681 } else { 682 out.writeInt(pkg.versionCode); 683 } 684 } 685 686 out.flush(); 687 } catch (IOException e) { 688 Slog.e(TAG, "Unable to write package manager state file!"); 689 } 690 } 691 692 interface RestoreDataConsumer { consumeRestoreData(BackupDataInput data)693 void consumeRestoreData(BackupDataInput data) throws IOException; 694 } 695 696 private class LegacyRestoreDataConsumer implements RestoreDataConsumer { 697 consumeRestoreData(BackupDataInput data)698 public void consumeRestoreData(BackupDataInput data) throws IOException { 699 List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>(); 700 HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>(); 701 int storedSystemVersion = -1; 702 703 if (DEBUG) Slog.i(TAG, "Using LegacyRestoreDataConsumer"); 704 // we already have the first header read and "cached", since ANCESTRAL_RECORD_KEY 705 // was missing 706 while (true) { 707 String key = data.getKey(); 708 int dataSize = data.getDataSize(); 709 710 if (DEBUG) Slog.v(TAG, " got key=" + key + " dataSize=" + dataSize); 711 712 // generic setup to parse any entity data 713 byte[] inputBytes = new byte[dataSize]; 714 data.readEntityData(inputBytes, 0, dataSize); 715 ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes); 716 DataInputStream inputBufferStream = new DataInputStream(inputBuffer); 717 718 if (key.equals(GLOBAL_METADATA_KEY)) { 719 int storedSdkVersion = inputBufferStream.readInt(); 720 if (DEBUG) Slog.v(TAG, " storedSystemVersion = " + storedSystemVersion); 721 mStoredSdkVersion = storedSdkVersion; 722 mStoredIncrementalVersion = inputBufferStream.readUTF(); 723 mHasMetadata = true; 724 if (DEBUG) { 725 Slog.i( 726 TAG, 727 "Restore set version " 728 + storedSystemVersion 729 + " is compatible with OS version " 730 + Build.VERSION.SDK_INT 731 + " (" 732 + mStoredIncrementalVersion 733 + " vs " 734 + Build.VERSION.INCREMENTAL 735 + ")"); 736 } 737 } else if (key.equals(DEFAULT_HOME_KEY)) { 738 String cn = inputBufferStream.readUTF(); 739 mRestoredHome = ComponentName.unflattenFromString(cn); 740 mRestoredHomeVersion = inputBufferStream.readLong(); 741 mRestoredHomeInstaller = inputBufferStream.readUTF(); 742 mRestoredHomeSigHashes = readSignatureHashArray(inputBufferStream); 743 if (DEBUG) { 744 Slog.i( 745 TAG, 746 " read preferred home app " 747 + mRestoredHome 748 + " version=" 749 + mRestoredHomeVersion 750 + " installer=" 751 + mRestoredHomeInstaller 752 + " sig=" 753 + mRestoredHomeSigHashes); 754 } 755 } else { 756 // it's a file metadata record 757 int versionCodeInt = inputBufferStream.readInt(); 758 long versionCode; 759 if (versionCodeInt == Integer.MIN_VALUE) { 760 versionCode = inputBufferStream.readLong(); 761 } else { 762 versionCode = versionCodeInt; 763 } 764 ArrayList<byte[]> sigs = readSignatureHashArray(inputBufferStream); 765 if (DEBUG) { 766 Slog.i( 767 TAG, 768 " read metadata for " 769 + key 770 + " dataSize=" 771 + dataSize 772 + " versionCode=" 773 + versionCode 774 + " sigs=" 775 + sigs); 776 } 777 778 if (sigs == null || sigs.size() == 0) { 779 Slog.w( 780 TAG, 781 "Not restoring package " 782 + key 783 + " since it appears to have no signatures."); 784 if (!data.readNextHeader()) { 785 break; 786 } 787 continue; 788 } 789 790 ApplicationInfo app = new ApplicationInfo(); 791 app.packageName = key; 792 restoredApps.add(app); 793 sigMap.put(key, new Metadata(versionCode, sigs)); 794 } 795 796 if (!data.readNextHeader()) { 797 break; 798 } 799 } 800 801 if (DEBUG) { 802 Slog.v(TAG, "LegacyRestoreDataConsumer:" + " we're done reading all the headers"); 803 } 804 805 // On successful completion, cache the signature map for the Backup Manager to use 806 mRestoredSignatures = sigMap; 807 } 808 } 809 810 private class AncestralVersion1RestoreDataConsumer implements RestoreDataConsumer { 811 consumeRestoreData(BackupDataInput data)812 public void consumeRestoreData(BackupDataInput data) throws IOException { 813 List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>(); 814 HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>(); 815 int storedSystemVersion = -1; 816 817 if (DEBUG) Slog.i(TAG, "Using AncestralVersion1RestoreDataConsumer"); 818 while (data.readNextHeader()) { 819 String key = data.getKey(); 820 int dataSize = data.getDataSize(); 821 822 if (DEBUG) Slog.v(TAG, " got key=" + key + " dataSize=" + dataSize); 823 824 // generic setup to parse any entity data 825 byte[] inputBytes = new byte[dataSize]; 826 data.readEntityData(inputBytes, 0, dataSize); 827 ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes); 828 DataInputStream inputBufferStream = new DataInputStream(inputBuffer); 829 830 if (key.equals(GLOBAL_METADATA_KEY)) { 831 int storedSdkVersion = inputBufferStream.readInt(); 832 if (DEBUG) Slog.v(TAG, " storedSystemVersion = " + storedSystemVersion); 833 mStoredSdkVersion = storedSdkVersion; 834 mStoredIncrementalVersion = inputBufferStream.readUTF(); 835 mHasMetadata = true; 836 if (DEBUG) { 837 Slog.i( 838 TAG, 839 "Restore set version " 840 + storedSystemVersion 841 + " is compatible with OS version " 842 + Build.VERSION.SDK_INT 843 + " (" 844 + mStoredIncrementalVersion 845 + " vs " 846 + Build.VERSION.INCREMENTAL 847 + ")"); 848 } 849 } else if (key.equals(DEFAULT_HOME_KEY)) { 850 // Default home app data is no longer backed up by this agent. This code is 851 // kept to handle restore of old backups that still contain home app data. 852 String cn = inputBufferStream.readUTF(); 853 mRestoredHome = ComponentName.unflattenFromString(cn); 854 mRestoredHomeVersion = inputBufferStream.readLong(); 855 mRestoredHomeInstaller = inputBufferStream.readUTF(); 856 mRestoredHomeSigHashes = readSignatureHashArray(inputBufferStream); 857 if (DEBUG) { 858 Slog.i( 859 TAG, 860 " read preferred home app " 861 + mRestoredHome 862 + " version=" 863 + mRestoredHomeVersion 864 + " installer=" 865 + mRestoredHomeInstaller 866 + " sig=" 867 + mRestoredHomeSigHashes); 868 } 869 } else { 870 // it's a file metadata record 871 int versionCodeInt = inputBufferStream.readInt(); 872 long versionCode; 873 if (versionCodeInt == Integer.MIN_VALUE) { 874 versionCode = inputBufferStream.readLong(); 875 } else { 876 versionCode = versionCodeInt; 877 } 878 ArrayList<byte[]> sigs = readSignatureHashArray(inputBufferStream); 879 if (DEBUG) { 880 Slog.i( 881 TAG, 882 " read metadata for " 883 + key 884 + " dataSize=" 885 + dataSize 886 + " versionCode=" 887 + versionCode 888 + " sigs=" 889 + sigs); 890 } 891 892 if (sigs == null || sigs.size() == 0) { 893 Slog.w( 894 TAG, 895 "Not restoring package " 896 + key 897 + " since it appears to have no signatures."); 898 continue; 899 } 900 901 ApplicationInfo app = new ApplicationInfo(); 902 app.packageName = key; 903 restoredApps.add(app); 904 sigMap.put(key, new Metadata(versionCode, sigs)); 905 } 906 } 907 908 // On successful completion, cache the signature map for the Backup Manager to use 909 mRestoredSignatures = sigMap; 910 } 911 } 912 } 913