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         try {
239             /*
240              * Global metadata:
241              *
242              * int SDKversion -- the SDK version of the OS itself on the device
243              *                   that produced this backup set. Before Android P it was used to
244              *                   reject backups from later OSes onto earlier ones.
245              * String incremental -- the incremental release name of the OS stored in
246              *                       the backup set.
247              */
248             outputBuffer.reset();
249             if (!mExisting.contains(GLOBAL_METADATA_KEY)) {
250                 if (DEBUG) Slog.v(TAG, "Storing global metadata key");
251                 outputBufferStream.writeInt(Build.VERSION.SDK_INT);
252                 outputBufferStream.writeUTF(Build.VERSION.INCREMENTAL);
253                 writeEntity(data, GLOBAL_METADATA_KEY, outputBuffer.toByteArray());
254             } else {
255                 if (DEBUG) Slog.v(TAG, "Global metadata key already stored");
256                 // don't consider it to have been skipped/deleted
257                 mExisting.remove(GLOBAL_METADATA_KEY);
258             }
259 
260             // For each app we have on device, see if we've backed it up yet.  If not,
261             // write its signature block to the output, keyed on the package name.
262             for (PackageInfo pkg : mAllPackages) {
263                 String packName = pkg.packageName;
264                 if (packName.equals(GLOBAL_METADATA_KEY)) {
265                     // We've already handled the metadata key; skip it here
266                     continue;
267                 } else {
268                     PackageInfo info = null;
269                     try {
270                         info = mPackageManager.getPackageInfoAsUser(packName,
271                                 PackageManager.GET_SIGNING_CERTIFICATES, mUserId);
272                     } catch (NameNotFoundException e) {
273                         // Weird; we just found it, and now are told it doesn't exist.
274                         // Treat it as having been removed from the device.
275                         mExisting.add(packName);
276                         continue;
277                     }
278 
279                     if (mExisting.contains(packName)) {
280                         // We have backed up this app before.  Check whether the version
281                         // of the backup matches the version of the current app; if they
282                         // don't match, the app has been updated and we need to store its
283                         // metadata again.  In either case, take it out of mExisting so that
284                         // we don't consider it deleted later.
285                         mExisting.remove(packName);
286                         if (info.getLongVersionCode() == mStateVersions.get(packName).versionCode) {
287                             continue;
288                         }
289                     }
290 
291                     SigningInfo signingInfo = info.signingInfo;
292                     if (signingInfo == null) {
293                         Slog.w(TAG, "Not backing up package " + packName
294                                 + " since it appears to have no signatures.");
295                         continue;
296                     }
297 
298                     // We need to store this app's metadata
299                     /*
300                      * Metadata for each package:
301                      *
302                      * int version       -- [4] the package's versionCode
303                      * byte[] signatures -- [len] flattened signature hash array of the package
304                      */
305 
306                     // marshal the version code in a canonical form
307                     outputBuffer.reset();
308                     if (info.versionCodeMajor != 0) {
309                         outputBufferStream.writeInt(Integer.MIN_VALUE);
310                         outputBufferStream.writeLong(info.getLongVersionCode());
311                     } else {
312                         outputBufferStream.writeInt(info.versionCode);
313                     }
314                     // retrieve the newest sigs to back up
315                     Signature[] infoSignatures = signingInfo.getApkContentsSigners();
316                     writeSignatureHashArray(outputBufferStream,
317                             BackupUtils.hashSignatureArray(infoSignatures));
318 
319                     if (DEBUG) {
320                         Slog.v(TAG, "+ writing metadata for " + packName
321                                 + " version=" + info.getLongVersionCode()
322                                 + " entityLen=" + outputBuffer.size());
323                     }
324 
325                     // Now we can write the backup entity for this package
326                     writeEntity(data, packName, outputBuffer.toByteArray());
327                 }
328             }
329 
330             // At this point, the only entries in 'existing' are apps that were
331             // mentioned in the saved state file, but appear to no longer be present
332             // on the device.  We want to preserve the entry for them, however,
333             // because we want the right thing to happen if the user goes through
334             // a backup / uninstall / backup / reinstall sequence.
335             if (DEBUG) {
336                 if (mExisting.size() > 0) {
337                     StringBuilder sb = new StringBuilder(64);
338                     sb.append("Preserving metadata for deleted packages:");
339                     for (String app : mExisting) {
340                         sb.append(' ');
341                         sb.append(app);
342                     }
343                     Slog.v(TAG, sb.toString());
344                 }
345             }
346         } catch (IOException e) {
347             // Real error writing data
348             Slog.e(TAG, "Unable to write package backup data file!");
349             return;
350         }
351 
352         // Finally, write the new state blob -- just the list of all apps we handled
353         writeStateFile(mAllPackages, newState);
354     }
355 
writeEntity(BackupDataOutput data, String key, byte[] bytes)356     private static void writeEntity(BackupDataOutput data, String key, byte[] bytes)
357             throws IOException {
358         data.writeEntityHeader(key, bytes.length);
359         data.writeEntityData(bytes, bytes.length);
360     }
361 
362     // "Restore" here is a misnomer.  What we're really doing is reading back the
363     // set of app signatures associated with each backed-up app in this restore
364     // image.  We'll use those later to determine what we can legitimately restore.
onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)365     public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
366             throws IOException {
367         if (DEBUG) Slog.v(TAG, "onRestore()");
368 
369         // we expect the ANCESTRAL_RECORD_KEY ("@ancestral_record@") to always come first in the
370         // restore set - based on that value we use different mechanisms to consume the data;
371         // if the ANCESTRAL_RECORD_KEY is missing in the restore set, it means that the data is
372         // is coming from a pre-Android P device, and we consume the header data in the legacy way
373         // TODO: add a CTS test to verify that backups of PMBA generated on Android P+ always
374         //       contain the ANCESTRAL_RECORD_KEY, and it's always the first key
375         int ancestralRecordVersion = getAncestralRecordVersionValue(data);
376 
377         RestoreDataConsumer consumer = getRestoreDataConsumer(ancestralRecordVersion);
378         if (consumer == null) {
379             Slog.w(TAG, "Ancestral restore set version is unknown"
380                     + " to this Android version; not restoring");
381             return;
382         } else {
383             consumer.consumeRestoreData(data);
384         }
385     }
386 
getAncestralRecordVersionValue(BackupDataInput data)387     private int getAncestralRecordVersionValue(BackupDataInput data) throws IOException {
388         int ancestralRecordVersionValue = UNDEFINED_ANCESTRAL_RECORD_VERSION;
389         if (data.readNextHeader()) {
390             String key = data.getKey();
391             int dataSize = data.getDataSize();
392 
393             if (DEBUG) Slog.v(TAG, "   got key=" + key + " dataSize=" + dataSize);
394 
395             if (ANCESTRAL_RECORD_KEY.equals(key)) {
396                 // generic setup to parse any entity data
397                 byte[] inputBytes = new byte[dataSize];
398                 data.readEntityData(inputBytes, 0, dataSize);
399                 ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes);
400                 DataInputStream inputBufferStream = new DataInputStream(inputBuffer);
401 
402                 ancestralRecordVersionValue = inputBufferStream.readInt();
403             }
404         }
405         return ancestralRecordVersionValue;
406     }
407 
getRestoreDataConsumer(int ancestralRecordVersion)408     private RestoreDataConsumer getRestoreDataConsumer(int ancestralRecordVersion) {
409         switch (ancestralRecordVersion) {
410             case UNDEFINED_ANCESTRAL_RECORD_VERSION:
411                 return new LegacyRestoreDataConsumer();
412             case 1:
413                 return new AncestralVersion1RestoreDataConsumer();
414             default:
415                 Slog.e(TAG, "Unrecognized ANCESTRAL_RECORD_VERSION: " + ancestralRecordVersion);
416                 return null;
417         }
418     }
419 
writeSignatureHashArray(DataOutputStream out, ArrayList<byte[]> hashes)420     private static void writeSignatureHashArray(DataOutputStream out, ArrayList<byte[]> hashes)
421             throws IOException {
422         // the number of entries in the array
423         out.writeInt(hashes.size());
424 
425         // the hash arrays themselves as length + contents
426         for (byte[] buffer : hashes) {
427             out.writeInt(buffer.length);
428             out.write(buffer);
429         }
430     }
431 
readSignatureHashArray(DataInputStream in)432     private static ArrayList<byte[]> readSignatureHashArray(DataInputStream in) {
433         try {
434             int num;
435             try {
436                 num = in.readInt();
437             } catch (EOFException e) {
438                 // clean termination
439                 Slog.w(TAG, "Read empty signature block");
440                 return null;
441             }
442 
443             if (DEBUG) Slog.v(TAG, " ... unflatten read " + num);
444 
445             // Sensical?
446             if (num > 20) {
447                 Slog.e(TAG, "Suspiciously large sig count in restore data; aborting");
448                 throw new IllegalStateException("Bad restore state");
449             }
450 
451             // This could be a "legacy" block of actual signatures rather than their hashes.
452             // If this is the case, convert them now.  We judge based on the payload size:
453             // if the blocks are all 256 bits (32 bytes) then we take them to be SHA-256 hashes;
454             // otherwise we take them to be Signatures.
455             boolean nonHashFound = false;
456             ArrayList<byte[]> sigs = new ArrayList<byte[]>(num);
457             for (int i = 0; i < num; i++) {
458                 int len = in.readInt();
459                 byte[] readHash = new byte[len];
460                 in.read(readHash);
461                 sigs.add(readHash);
462                 if (len != 32) {
463                     nonHashFound = true;
464                 }
465             }
466 
467             if (nonHashFound) {
468                 // Replace with the hashes.
469                 sigs = BackupUtils.hashSignatureArray(sigs);
470             }
471 
472             return sigs;
473         } catch (IOException e) {
474             Slog.e(TAG, "Unable to read signatures");
475             return null;
476         }
477     }
478 
479     // Util: parse out an existing state file into a usable structure
parseStateFile(ParcelFileDescriptor stateFile)480     private void parseStateFile(ParcelFileDescriptor stateFile) {
481         mExisting.clear();
482         mStateVersions.clear();
483         mStoredSdkVersion = 0;
484         mStoredIncrementalVersion = null;
485         mStoredHomeComponent = null;
486         mStoredHomeVersion = 0;
487         mStoredHomeSigHashes = null;
488 
489         // The state file is just the list of app names we have stored signatures for
490         // with the exception of the metadata block, to which is also appended the
491         // version numbers corresponding with the last time we wrote this PM block.
492         // If they mismatch the current system, we'll re-store the metadata key.
493         FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor());
494         BufferedInputStream inbuffer = new BufferedInputStream(instream);
495         DataInputStream in = new DataInputStream(inbuffer);
496 
497         try {
498             boolean ignoreExisting = false;
499             String pkg = in.readUTF();
500 
501             // Validate the state file version is sensical to us
502             if (pkg.equals(STATE_FILE_HEADER)) {
503                 int stateVersion = in.readInt();
504                 if (stateVersion > STATE_FILE_VERSION) {
505                     Slog.w(TAG, "Unsupported state file version " + stateVersion
506                             + ", redoing from start");
507                     return;
508                 }
509                 pkg = in.readUTF();
510             } else {
511                 // This is an older version of the state file in which the lead element
512                 // is not a STATE_FILE_VERSION string.  If that's the case, we want to
513                 // make sure to write our full backup dataset when given an opportunity.
514                 // We trigger that by simply not marking the restored package metadata
515                 // as known-to-exist-in-archive.
516                 Slog.i(TAG, "Older version of saved state - rewriting");
517                 ignoreExisting = true;
518             }
519 
520             // First comes the preferred home app data, if any, headed by the DEFAULT_HOME_KEY tag
521             if (pkg.equals(DEFAULT_HOME_KEY)) {
522                 // flattened component name, version, signature of the home app
523                 mStoredHomeComponent = ComponentName.unflattenFromString(in.readUTF());
524                 mStoredHomeVersion = in.readLong();
525                 mStoredHomeSigHashes = readSignatureHashArray(in);
526 
527                 pkg = in.readUTF(); // set up for the next block of state
528             } else {
529                 // else no preferred home app on the ancestral device - fall through to the rest
530             }
531 
532             // After (possible) home app data comes the global metadata block
533             if (pkg.equals(GLOBAL_METADATA_KEY)) {
534                 mStoredSdkVersion = in.readInt();
535                 mStoredIncrementalVersion = in.readUTF();
536                 if (!ignoreExisting) {
537                     mExisting.add(GLOBAL_METADATA_KEY);
538                 }
539             } else {
540                 Slog.e(TAG, "No global metadata in state file!");
541                 return;
542             }
543 
544             // The global metadata was last; now read all the apps
545             while (true) {
546                 pkg = in.readUTF();
547                 int versionCodeInt = in.readInt();
548                 long versionCode;
549                 if (versionCodeInt == Integer.MIN_VALUE) {
550                     versionCode = in.readLong();
551                 } else {
552                     versionCode = versionCodeInt;
553                 }
554 
555                 if (!ignoreExisting) {
556                     mExisting.add(pkg);
557                 }
558                 mStateVersions.put(pkg, new Metadata(versionCode, null));
559             }
560         } catch (EOFException eof) {
561             // safe; we're done
562         } catch (IOException e) {
563             // whoops, bad state file.  abort.
564             Slog.e(TAG, "Unable to read Package Manager state file: " + e);
565         }
566     }
567 
getPreferredHomeComponent()568     private ComponentName getPreferredHomeComponent() {
569         return mPackageManager.getHomeActivities(new ArrayList<ResolveInfo>());
570     }
571 
572     // Util: write out our new backup state file
writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile)573     private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
574         FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
575         BufferedOutputStream outbuf = new BufferedOutputStream(outstream);
576         DataOutputStream out = new DataOutputStream(outbuf);
577 
578         // by the time we get here we know we've done all our backing up
579         try {
580             // state file version header
581             out.writeUTF(STATE_FILE_HEADER);
582             out.writeInt(STATE_FILE_VERSION);
583 
584             // Conclude with the metadata block
585             out.writeUTF(GLOBAL_METADATA_KEY);
586             out.writeInt(Build.VERSION.SDK_INT);
587             out.writeUTF(Build.VERSION.INCREMENTAL);
588 
589             // now write all the app names + versions
590             for (PackageInfo pkg : pkgs) {
591                 out.writeUTF(pkg.packageName);
592                 if (pkg.versionCodeMajor != 0) {
593                     out.writeInt(Integer.MIN_VALUE);
594                     out.writeLong(pkg.getLongVersionCode());
595                 } else {
596                     out.writeInt(pkg.versionCode);
597                 }
598             }
599 
600             out.flush();
601         } catch (IOException e) {
602             Slog.e(TAG, "Unable to write package manager state file!");
603         }
604     }
605 
606     interface RestoreDataConsumer {
consumeRestoreData(BackupDataInput data)607         void consumeRestoreData(BackupDataInput data) throws IOException;
608     }
609 
610     private class LegacyRestoreDataConsumer implements RestoreDataConsumer {
611 
consumeRestoreData(BackupDataInput data)612         public void consumeRestoreData(BackupDataInput data) throws IOException {
613             List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
614             HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>();
615             int storedSystemVersion = -1;
616 
617             if (DEBUG) Slog.i(TAG, "Using LegacyRestoreDataConsumer");
618             // we already have the first header read and "cached", since ANCESTRAL_RECORD_KEY
619             // was missing
620             while (true) {
621                 String key = data.getKey();
622                 int dataSize = data.getDataSize();
623 
624                 if (DEBUG) Slog.v(TAG, "   got key=" + key + " dataSize=" + dataSize);
625 
626                 // generic setup to parse any entity data
627                 byte[] inputBytes = new byte[dataSize];
628                 data.readEntityData(inputBytes, 0, dataSize);
629                 ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes);
630                 DataInputStream inputBufferStream = new DataInputStream(inputBuffer);
631 
632                 if (key.equals(GLOBAL_METADATA_KEY)) {
633                     int storedSdkVersion = inputBufferStream.readInt();
634                     if (DEBUG) Slog.v(TAG, "   storedSystemVersion = " + storedSystemVersion);
635                     mStoredSdkVersion = storedSdkVersion;
636                     mStoredIncrementalVersion = inputBufferStream.readUTF();
637                     mHasMetadata = true;
638                     if (DEBUG) {
639                         Slog.i(TAG, "Restore set version " + storedSystemVersion
640                                 + " is compatible with OS version " + Build.VERSION.SDK_INT
641                                 + " (" + mStoredIncrementalVersion + " vs "
642                                 + Build.VERSION.INCREMENTAL + ")");
643                     }
644                 } else if (key.equals(DEFAULT_HOME_KEY)) {
645                     String cn = inputBufferStream.readUTF();
646                     mRestoredHome = ComponentName.unflattenFromString(cn);
647                     mRestoredHomeVersion = inputBufferStream.readLong();
648                     mRestoredHomeInstaller = inputBufferStream.readUTF();
649                     mRestoredHomeSigHashes = readSignatureHashArray(inputBufferStream);
650                     if (DEBUG) {
651                         Slog.i(TAG, "   read preferred home app " + mRestoredHome
652                                 + " version=" + mRestoredHomeVersion
653                                 + " installer=" + mRestoredHomeInstaller
654                                 + " sig=" + mRestoredHomeSigHashes);
655                     }
656                 } else {
657                     // it's a file metadata record
658                     int versionCodeInt = inputBufferStream.readInt();
659                     long versionCode;
660                     if (versionCodeInt == Integer.MIN_VALUE) {
661                         versionCode = inputBufferStream.readLong();
662                     } else {
663                         versionCode = versionCodeInt;
664                     }
665                     ArrayList<byte[]> sigs = readSignatureHashArray(inputBufferStream);
666                     if (DEBUG) {
667                         Slog.i(TAG, "   read metadata for " + key
668                                 + " dataSize=" + dataSize
669                                 + " versionCode=" + versionCode + " sigs=" + sigs);
670                     }
671 
672                     if (sigs == null || sigs.size() == 0) {
673                         Slog.w(TAG, "Not restoring package " + key
674                                 + " since it appears to have no signatures.");
675                         continue;
676                     }
677 
678                     ApplicationInfo app = new ApplicationInfo();
679                     app.packageName = key;
680                     restoredApps.add(app);
681                     sigMap.put(key, new Metadata(versionCode, sigs));
682                 }
683 
684                 boolean readNextHeader = data.readNextHeader();
685                 if (!readNextHeader) {
686                     if (DEBUG) Slog.v(TAG, "LegacyRestoreDataConsumer:"
687                             + " we're done reading all the headers");
688                     break;
689                 }
690             }
691 
692             // On successful completion, cache the signature map for the Backup Manager to use
693             mRestoredSignatures = sigMap;
694         }
695     }
696 
697     private class AncestralVersion1RestoreDataConsumer implements RestoreDataConsumer {
698 
consumeRestoreData(BackupDataInput data)699         public void consumeRestoreData(BackupDataInput data) throws IOException {
700             List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
701             HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>();
702             int storedSystemVersion = -1;
703 
704             if (DEBUG) Slog.i(TAG, "Using AncestralVersion1RestoreDataConsumer");
705             while (data.readNextHeader()) {
706                 String key = data.getKey();
707                 int dataSize = data.getDataSize();
708 
709                 if (DEBUG) Slog.v(TAG, "   got key=" + key + " dataSize=" + dataSize);
710 
711                 // generic setup to parse any entity data
712                 byte[] inputBytes = new byte[dataSize];
713                 data.readEntityData(inputBytes, 0, dataSize);
714                 ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes);
715                 DataInputStream inputBufferStream = new DataInputStream(inputBuffer);
716 
717                 if (key.equals(GLOBAL_METADATA_KEY)) {
718                     int storedSdkVersion = inputBufferStream.readInt();
719                     if (DEBUG) Slog.v(TAG, "   storedSystemVersion = " + storedSystemVersion);
720                     mStoredSdkVersion = storedSdkVersion;
721                     mStoredIncrementalVersion = inputBufferStream.readUTF();
722                     mHasMetadata = true;
723                     if (DEBUG) {
724                         Slog.i(TAG, "Restore set version " + storedSystemVersion
725                                 + " is compatible with OS version " + Build.VERSION.SDK_INT
726                                 + " (" + mStoredIncrementalVersion + " vs "
727                                 + Build.VERSION.INCREMENTAL + ")");
728                     }
729                 } else if (key.equals(DEFAULT_HOME_KEY)) {
730                     // Default home app data is no longer backed up by this agent. This code is
731                     // kept to handle restore of old backups that still contain home app data.
732                     String cn = inputBufferStream.readUTF();
733                     mRestoredHome = ComponentName.unflattenFromString(cn);
734                     mRestoredHomeVersion = inputBufferStream.readLong();
735                     mRestoredHomeInstaller = inputBufferStream.readUTF();
736                     mRestoredHomeSigHashes = readSignatureHashArray(inputBufferStream);
737                     if (DEBUG) {
738                         Slog.i(TAG, "   read preferred home app " + mRestoredHome
739                                 + " version=" + mRestoredHomeVersion
740                                 + " installer=" + mRestoredHomeInstaller
741                                 + " sig=" + mRestoredHomeSigHashes);
742                     }
743                 } else {
744                     // it's a file metadata record
745                     int versionCodeInt = inputBufferStream.readInt();
746                     long versionCode;
747                     if (versionCodeInt == Integer.MIN_VALUE) {
748                         versionCode = inputBufferStream.readLong();
749                     } else {
750                         versionCode = versionCodeInt;
751                     }
752                     ArrayList<byte[]> sigs = readSignatureHashArray(inputBufferStream);
753                     if (DEBUG) {
754                         Slog.i(TAG, "   read metadata for " + key
755                                 + " dataSize=" + dataSize
756                                 + " versionCode=" + versionCode + " sigs=" + sigs);
757                     }
758 
759                     if (sigs == null || sigs.size() == 0) {
760                         Slog.w(TAG, "Not restoring package " + key
761                                 + " since it appears to have no signatures.");
762                         continue;
763                     }
764 
765                     ApplicationInfo app = new ApplicationInfo();
766                     app.packageName = key;
767                     restoredApps.add(app);
768                     sigMap.put(key, new Metadata(versionCode, sigs));
769                 }
770             }
771 
772             // On successful completion, cache the signature map for the Backup Manager to use
773             mRestoredSignatures = sigMap;
774         }
775     }
776 }
777