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