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