Home | History | Annotate | Download | only in backup
      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