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