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