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