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