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 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. 132 public PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) { 133 init(packageMgr, packages); 134 } 135 136 public PackageManagerBackupAgent(PackageManager packageMgr) { 137 init(packageMgr, null); 138 139 evaluateStorablePackages(); 140 } 141 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. 154 public void evaluateStorablePackages() { 155 mAllPackages = getStorableApplications(mPackageManager); 156 } 157 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 171 public boolean hasMetadata() { 172 return mHasMetadata; 173 } 174 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 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. 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 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. 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 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 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 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 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 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 616 private ComponentName getPreferredHomeComponent() { 617 return mPackageManager.getHomeActivities(new ArrayList<ResolveInfo>()); 618 } 619 620 // Util: write out our new backup state file 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 { 664 void consumeRestoreData(BackupDataInput data) throws IOException; 665 } 666 667 private class LegacyRestoreDataConsumer implements RestoreDataConsumer { 668 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 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