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