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