16785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate/*
26785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * Copyright (C) 2009 The Android Open Source Project
36785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate *
46785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * Licensed under the Apache License, Version 2.0 (the "License");
56785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * you may not use this file except in compliance with the License.
66785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * You may obtain a copy of the License at
76785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate *
86785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate *      http://www.apache.org/licenses/LICENSE-2.0
96785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate *
106785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * Unless required by applicable law or agreed to in writing, software
116785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * distributed under the License is distributed on an "AS IS" BASIS,
126785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * See the License for the specific language governing permissions and
146785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * limitations under the License.
156785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate */
166785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
1709e9cdceceb722643e2c80c6544d44a43d7f95f0Amith Yamasanipackage com.android.server.backup;
186785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
194528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tateimport android.app.backup.BackupAgent;
204528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tateimport android.app.backup.BackupDataInput;
214528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tateimport android.app.backup.BackupDataOutput;
22a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tateimport android.content.ComponentName;
236785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport android.content.pm.ApplicationInfo;
246785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport android.content.pm.PackageInfo;
256785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport android.content.pm.PackageManager;
26a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tateimport android.content.pm.ResolveInfo;
276785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport android.content.pm.PackageManager.NameNotFoundException;
286785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport android.content.pm.Signature;
293a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tateimport android.os.Build;
306785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport android.os.ParcelFileDescriptor;
318a9b22056b13477f59df934928c00c58b5871c95Joe Onoratoimport android.util.Slog;
326785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
33a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tateimport java.io.BufferedInputStream;
34a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tateimport java.io.BufferedOutputStream;
356785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport java.io.ByteArrayInputStream;
366785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport java.io.ByteArrayOutputStream;
376785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport java.io.DataInputStream;
386785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport java.io.DataOutputStream;
396785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport java.io.EOFException;
406785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport java.io.FileInputStream;
416785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport java.io.FileOutputStream;
426785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport java.io.IOException;
436785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport java.util.ArrayList;
446785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport java.util.HashMap;
456785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport java.util.HashSet;
466785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tateimport java.util.List;
47b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tateimport java.util.Set;
486785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
49a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tateimport java.util.Objects;
50a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate
516785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate/**
526785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * We back up the signatures of each package so that during a system restore,
536785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * we can verify that the app whose data we think we have matches the app
546785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * actually resident on the device.
556785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate *
566785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * Since the Package Manager isn't a proper "application" we just provide a
576785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate * direct IBackupAgent implementation and hand-construct it at need.
586785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate */
596785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tatepublic class PackageManagerBackupAgent extends BackupAgent {
606785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    private static final String TAG = "PMBA";
61b808a939328b935592d9259e06c66b433a13c1a9Christopher Tate    private static final boolean DEBUG = false;
626785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
633a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate    // key under which we store global metadata (individual app metadata
643a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate    // is stored using the package name as a key)
653a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate    private static final String GLOBAL_METADATA_KEY = "@meta@";
663a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate
67a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate    // key under which we store the identity of the user's chosen default home app
68a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate    private static final String DEFAULT_HOME_KEY = "@home@";
69a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate
7051fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    // Sentinel: start of state file, followed by a version number
7151fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    private static final String STATE_FILE_HEADER = "=state=";
7251fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    private static final int STATE_FILE_VERSION = 2;
7351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
7451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    // Current version of the saved ancestral-dataset file format
7551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    private static final int ANCESTRAL_RECORD_VERSION = 1;
7651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
77efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    private List<PackageInfo> mAllPackages;
786785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    private PackageManager mPackageManager;
7972d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate    // version & signature info of each app in a restore set
806aa41f4c575479672661f7eb4c704ef59d26a629Christopher Tate    private HashMap<String, Metadata> mRestoredSignatures;
8172d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate    // The version info of each backed-up app as read from the state file
8272d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate    private HashMap<String, Metadata> mStateVersions = new HashMap<String, Metadata>();
8372d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate
8472d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate    private final HashSet<String> mExisting = new HashSet<String>();
8572d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate    private int mStoredSdkVersion;
8672d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate    private String mStoredIncrementalVersion;
87a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate    private ComponentName mStoredHomeComponent;
88a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate    private long mStoredHomeVersion;
8951fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    private ArrayList<byte[]> mStoredHomeSigHashes;
90a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate
913d7cd13e772bde1c4a72fa4e740baa03cb042e6cChristopher Tate    private boolean mHasMetadata;
92a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate    private ComponentName mRestoredHome;
93a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate    private long mRestoredHomeVersion;
94a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate    private String mRestoredHomeInstaller;
9551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    private ArrayList<byte[]> mRestoredHomeSigHashes;
966aa41f4c575479672661f7eb4c704ef59d26a629Christopher Tate
9751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    // For compactness we store the SHA-256 hash of each app's Signatures
9851fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    // rather than the Signature blocks themselves.
996aa41f4c575479672661f7eb4c704ef59d26a629Christopher Tate    public class Metadata {
1006aa41f4c575479672661f7eb4c704ef59d26a629Christopher Tate        public int versionCode;
10151fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        public ArrayList<byte[]> sigHashes;
1026aa41f4c575479672661f7eb4c704ef59d26a629Christopher Tate
10351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        Metadata(int version, ArrayList<byte[]> hashes) {
1046aa41f4c575479672661f7eb4c704ef59d26a629Christopher Tate            versionCode = version;
10551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            sigHashes = hashes;
1066aa41f4c575479672661f7eb4c704ef59d26a629Christopher Tate        }
1076aa41f4c575479672661f7eb4c704ef59d26a629Christopher Tate    }
1086785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
1096785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    // We're constructed with the set of applications that are participating
1106785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    // in backup.  This set changes as apps are installed & removed.
111efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
11251fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        init(packageMgr, packages);
11351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    }
11451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
11551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    PackageManagerBackupAgent(PackageManager packageMgr) {
11651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        init(packageMgr, null);
11751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
11851fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        evaluateStorablePackages();
11951fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    }
12051fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
12151fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    private void init(PackageManager packageMgr, List<PackageInfo> packages) {
1226785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        mPackageManager = packageMgr;
123efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        mAllPackages = packages;
1246785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        mRestoredSignatures = null;
1253d7cd13e772bde1c4a72fa4e740baa03cb042e6cChristopher Tate        mHasMetadata = false;
12651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
12751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        mStoredSdkVersion = Build.VERSION.SDK_INT;
12851fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        mStoredIncrementalVersion = Build.VERSION.INCREMENTAL;
12951fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    }
13051fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
13151fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    // We will need to refresh our understanding of what is eligible for
13251fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    // backup periodically; this entry point serves that purpose.
13351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    public void evaluateStorablePackages() {
13451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        mAllPackages = getStorableApplications(mPackageManager);
13551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    }
13651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
13751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    public static List<PackageInfo> getStorableApplications(PackageManager pm) {
13851fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        List<PackageInfo> pkgs;
13951fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        pkgs = pm.getInstalledPackages(PackageManager.GET_SIGNATURES);
14051fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        int N = pkgs.size();
14151fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        for (int a = N-1; a >= 0; a--) {
14251fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            PackageInfo pkg = pkgs.get(a);
14351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            if (!BackupManagerService.appIsEligibleForBackup(pkg.applicationInfo)) {
14451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                pkgs.remove(a);
14551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            }
14651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        }
14751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        return pkgs;
1483d7cd13e772bde1c4a72fa4e740baa03cb042e6cChristopher Tate    }
1493d7cd13e772bde1c4a72fa4e740baa03cb042e6cChristopher Tate
1503d7cd13e772bde1c4a72fa4e740baa03cb042e6cChristopher Tate    public boolean hasMetadata() {
1513d7cd13e772bde1c4a72fa4e740baa03cb042e6cChristopher Tate        return mHasMetadata;
1526785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    }
1536785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
1546aa41f4c575479672661f7eb4c704ef59d26a629Christopher Tate    public Metadata getRestoredMetadata(String packageName) {
1556785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        if (mRestoredSignatures == null) {
1568a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            Slog.w(TAG, "getRestoredMetadata() before metadata read!");
1576785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            return null;
1586785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        }
1596785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
1606785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        return mRestoredSignatures.get(packageName);
1616785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    }
162b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tate
163b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tate    public Set<String> getRestoredPackages() {
164b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tate        if (mRestoredSignatures == null) {
1658a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            Slog.w(TAG, "getRestoredPackages() before metadata read!");
166b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tate            return null;
167b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tate        }
168b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tate
169b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tate        // This is technically the set of packages on the originating handset
170b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tate        // that had backup agents at all, not limited to the set of packages
171b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tate        // that had actually contributed a restore dataset, but it's a
172b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tate        // close enough approximation for our purposes and does not require any
173b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tate        // additional involvement by the transport to obtain.
174b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tate        return mRestoredSignatures.keySet();
175b49ceb3b8b17656984fd969d548dc912e7d2c7c1Christopher Tate    }
1766785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
1776785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    // The backed up data is the signature block for each app, keyed by
1786785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    // the package name.
1796785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
1806785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            ParcelFileDescriptor newState) {
1818a9b22056b13477f59df934928c00c58b5871c95Joe Onorato        if (DEBUG) Slog.v(TAG, "onBackup()");
1823a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate
183e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown        ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();  // we'll reuse these
184e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown        DataOutputStream outputBufferStream = new DataOutputStream(outputBuffer);
18572d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate        parseStateFile(oldState);
18672d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate
18772d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate        // If the stored version string differs, we need to re-backup all
18872d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate        // of the metadata.  We force this by removing everything from the
18972d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate        // "already backed up" map built by parseStateFile().
19072d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate        if (mStoredIncrementalVersion == null
19172d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                || !mStoredIncrementalVersion.equals(Build.VERSION.INCREMENTAL)) {
1928a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            Slog.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs "
19372d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                    + Build.VERSION.INCREMENTAL + " - rewriting");
19472d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate            mExisting.clear();
19572d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate        }
1963a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate
197a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        long homeVersion = 0;
19851fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        ArrayList<byte[]> homeSigHashes = null;
199a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        PackageInfo homeInfo = null;
200a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        String homeInstaller = null;
201a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        ComponentName home = getPreferredHomeComponent();
202a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        if (home != null) {
203a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            try {
204a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                homeInfo = mPackageManager.getPackageInfo(home.getPackageName(),
205a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                        PackageManager.GET_SIGNATURES);
206a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
207a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                homeVersion = homeInfo.versionCode;
20851fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                homeSigHashes = hashSignatureArray(homeInfo.signatures);
209a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            } catch (NameNotFoundException e) {
210a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                Slog.w(TAG, "Can't access preferred home info");
211a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                // proceed as though there were no preferred home set
212a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                home = null;
213a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            }
214a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        }
215a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate
2163a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate        try {
217a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            // We need to push a new preferred-home-app record if:
218a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            //    1. the version of the home app has changed since our last backup;
219a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            //    2. the home app [or absence] we now use differs from the prior state,
220a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            // OR 3. it looks like we use the same home app + version as before, but
221a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            //       the signatures don't match so we treat them as different apps.
222a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            final boolean needHomeBackup = (homeVersion != mStoredHomeVersion)
22322192ada99691221f9ab167c046fb71c23baf723Christopher Tate                    || !Objects.equals(home, mStoredHomeComponent)
224a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                    || (home != null
22551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                        && !BackupManagerService.signaturesMatch(mStoredHomeSigHashes, homeInfo));
226a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            if (needHomeBackup) {
227a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                if (DEBUG) {
228a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                    Slog.i(TAG, "Home preference changed; backing up new state " + home);
229a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                }
230a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                if (home != null) {
231a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                    outputBufferStream.writeUTF(home.flattenToString());
232a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                    outputBufferStream.writeLong(homeVersion);
233d4659698fe2c8d854089138000ec24193d8359b8Christopher Tate                    outputBufferStream.writeUTF(homeInstaller != null ? homeInstaller : "" );
23451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                    writeSignatureHashArray(outputBufferStream, homeSigHashes);
235a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                    writeEntity(data, DEFAULT_HOME_KEY, outputBuffer.toByteArray());
236a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                } else {
237a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                    data.writeEntityHeader(DEFAULT_HOME_KEY, -1);
238a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                }
239a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            }
240a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate
2413a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            /*
2423a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate             * Global metadata:
2433a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate             *
24472d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate             * int SDKversion -- the SDK version of the OS itself on the device
24572d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate             *                   that produced this backup set.  Used to reject
24672d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate             *                   backups from later OSes onto earlier ones.
24772d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate             * String incremental -- the incremental release name of the OS stored in
24872d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate             *                       the backup set.
2493a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate             */
250a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            outputBuffer.reset();
25172d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate            if (!mExisting.contains(GLOBAL_METADATA_KEY)) {
2528a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                if (DEBUG) Slog.v(TAG, "Storing global metadata key");
253e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                outputBufferStream.writeInt(Build.VERSION.SDK_INT);
254e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                outputBufferStream.writeUTF(Build.VERSION.INCREMENTAL);
255e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                writeEntity(data, GLOBAL_METADATA_KEY, outputBuffer.toByteArray());
2566785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            } else {
2578a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                if (DEBUG) Slog.v(TAG, "Global metadata key already stored");
25872d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                // don't consider it to have been skipped/deleted
25972d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                mExisting.remove(GLOBAL_METADATA_KEY);
2603a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            }
2613a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate
2623a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            // For each app we have on device, see if we've backed it up yet.  If not,
2633a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            // write its signature block to the output, keyed on the package name.
264efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            for (PackageInfo pkg : mAllPackages) {
265efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                String packName = pkg.packageName;
2666f317426e49e73ef3e50d8839877504039cd2fcaChristopher Tate                if (packName.equals(GLOBAL_METADATA_KEY)) {
2676f317426e49e73ef3e50d8839877504039cd2fcaChristopher Tate                    // We've already handled the metadata key; skip it here
2686f317426e49e73ef3e50d8839877504039cd2fcaChristopher Tate                    continue;
26972d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                } else {
27072d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                    PackageInfo info = null;
2713a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                    try {
27272d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                        info = mPackageManager.getPackageInfo(packName,
2733a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                                PackageManager.GET_SIGNATURES);
27472d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                    } catch (NameNotFoundException e) {
27572d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                        // Weird; we just found it, and now are told it doesn't exist.
27672d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                        // Treat it as having been removed from the device.
27772d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                        mExisting.add(packName);
27872d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                        continue;
27972d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                    }
28072d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate
281e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    if (mExisting.contains(packName)) {
282e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                        // We have backed up this app before.  Check whether the version
28372d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                        // of the backup matches the version of the current app; if they
28472d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                        // don't match, the app has been updated and we need to store its
28572d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                        // metadata again.  In either case, take it out of mExisting so that
28672d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                        // we don't consider it deleted later.
28772d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                        mExisting.remove(packName);
288e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                        if (info.versionCode == mStateVersions.get(packName).versionCode) {
289e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                            continue;
290e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                        }
291e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    }
292e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown
293e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    if (info.signatures == null || info.signatures.length == 0)
294e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    {
295e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                        Slog.w(TAG, "Not backing up package " + packName
296e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                                + " since it appears to have no signatures.");
297e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                        continue;
29872d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                    }
29972d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate
300e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    // We need to store this app's metadata
301e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    /*
302e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                     * Metadata for each package:
303e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                     *
304e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                     * int version       -- [4] the package's versionCode
30551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                     * byte[] signatures -- [len] flattened signature hash array of the package
306e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                     */
307e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown
308e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    // marshal the version code in a canonical form
309e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    outputBuffer.reset();
310e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    outputBufferStream.writeInt(info.versionCode);
31151fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                    writeSignatureHashArray(outputBufferStream,
31251fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                            hashSignatureArray(info.signatures));
313e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown
314e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    if (DEBUG) {
315e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                        Slog.v(TAG, "+ writing metadata for " + packName
316e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                                + " version=" + info.versionCode
317e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                                + " entityLen=" + outputBuffer.size());
3183a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                    }
319e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown
320e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    // Now we can write the backup entity for this package
321e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    writeEntity(data, packName, outputBuffer.toByteArray());
3226785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate                }
3236785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            }
3246785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
3253a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            // At this point, the only entries in 'existing' are apps that were
3263a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            // mentioned in the saved state file, but appear to no longer be present
3273a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            // on the device.  Write a deletion entity for them.
32872d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate            for (String app : mExisting) {
3298a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                if (DEBUG) Slog.v(TAG, "- removing metadata for deleted pkg " + app);
3303a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                try {
3313a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                    data.writeEntityHeader(app, -1);
3323a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                } catch (IOException e) {
3338a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                    Slog.e(TAG, "Unable to write package deletions!");
3343a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                    return;
3353a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                }
3366785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            }
3373a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate        } catch (IOException e) {
3383a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            // Real error writing data
3398a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            Slog.e(TAG, "Unable to write package backup data file!");
3403a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            return;
3416785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        }
3426785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
3436785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        // Finally, write the new state blob -- just the list of all apps we handled
34451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        writeStateFile(mAllPackages, home, homeVersion, homeSigHashes, newState);
3456785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    }
346e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown
347e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown    private static void writeEntity(BackupDataOutput data, String key, byte[] bytes)
348e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown            throws IOException {
349e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown        data.writeEntityHeader(key, bytes.length);
350e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown        data.writeEntityData(bytes, bytes.length);
351e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown    }
3526785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
3536785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    // "Restore" here is a misnomer.  What we're really doing is reading back the
3546785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    // set of app signatures associated with each backed-up app in this restore
3556785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    // image.  We'll use those later to determine what we can legitimately restore.
3565cbbf5652a78902ac3382dc4a3583bc5b0351027Christopher Tate    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
3576785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            throws IOException {
3586785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
3596aa41f4c575479672661f7eb4c704ef59d26a629Christopher Tate        HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>();
3608a9b22056b13477f59df934928c00c58b5871c95Joe Onorato        if (DEBUG) Slog.v(TAG, "onRestore()");
3613a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate        int storedSystemVersion = -1;
3626785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
3636785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        while (data.readNextHeader()) {
3643a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            String key = data.getKey();
3656785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            int dataSize = data.getDataSize();
3666785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
3678a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            if (DEBUG) Slog.v(TAG, "   got key=" + key + " dataSize=" + dataSize);
3683a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate
3693a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            // generic setup to parse any entity data
370e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown            byte[] inputBytes = new byte[dataSize];
371e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown            data.readEntityData(inputBytes, 0, dataSize);
372e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown            ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes);
373e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown            DataInputStream inputBufferStream = new DataInputStream(inputBuffer);
3743a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate
3753a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            if (key.equals(GLOBAL_METADATA_KEY)) {
376e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                int storedSdkVersion = inputBufferStream.readInt();
3778a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                if (DEBUG) Slog.v(TAG, "   storedSystemVersion = " + storedSystemVersion);
3783a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                if (storedSystemVersion > Build.VERSION.SDK_INT) {
3793a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                    // returning before setting the sig map means we rejected the restore set
3808a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                    Slog.w(TAG, "Restore set was from a later version of Android; not restoring");
3813a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                    return;
3823a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                }
38372d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                mStoredSdkVersion = storedSdkVersion;
384e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                mStoredIncrementalVersion = inputBufferStream.readUTF();
3853d7cd13e772bde1c4a72fa4e740baa03cb042e6cChristopher Tate                mHasMetadata = true;
3863a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                if (DEBUG) {
3878a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                    Slog.i(TAG, "Restore set version " + storedSystemVersion
38872d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                            + " is compatible with OS version " + Build.VERSION.SDK_INT
38972d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                            + " (" + mStoredIncrementalVersion + " vs "
39072d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                            + Build.VERSION.INCREMENTAL + ")");
3913a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                }
392a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            } else if (key.equals(DEFAULT_HOME_KEY)) {
393a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                String cn = inputBufferStream.readUTF();
394a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                mRestoredHome = ComponentName.unflattenFromString(cn);
395a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                mRestoredHomeVersion = inputBufferStream.readLong();
396a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                mRestoredHomeInstaller = inputBufferStream.readUTF();
39751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                mRestoredHomeSigHashes = readSignatureHashArray(inputBufferStream);
398a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                if (DEBUG) {
399a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                    Slog.i(TAG, "   read preferred home app " + mRestoredHome
400a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                            + " version=" + mRestoredHomeVersion
40151fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                            + " installer=" + mRestoredHomeInstaller
40251fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                            + " sig=" + mRestoredHomeSigHashes);
403a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                }
4043a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            } else {
4053a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                // it's a file metadata record
406e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                int versionCode = inputBufferStream.readInt();
40751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                ArrayList<byte[]> sigs = readSignatureHashArray(inputBufferStream);
4083a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                if (DEBUG) {
409e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    Slog.i(TAG, "   read metadata for " + key
4103a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                            + " dataSize=" + dataSize
4113a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                            + " versionCode=" + versionCode + " sigs=" + sigs);
4123a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                }
413e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown
41451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                if (sigs == null || sigs.size() == 0) {
415e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    Slog.w(TAG, "Not restoring package " + key
416e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                            + " since it appears to have no signatures.");
417e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                    continue;
418e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                }
4193a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate
4203a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                ApplicationInfo app = new ApplicationInfo();
4213a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                app.packageName = key;
4223a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                restoredApps.add(app);
4233a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate                sigMap.put(key, new Metadata(versionCode, sigs));
4243a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate            }
4256785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        }
4266785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
4273a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate        // On successful completion, cache the signature map for the Backup Manager to use
4286785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        mRestoredSignatures = sigMap;
4296785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    }
4306785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
43151fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    private static ArrayList<byte[]> hashSignatureArray(Signature[] sigs) {
43251fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        if (sigs == null) {
43351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            return null;
43451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        }
43551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
43651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        ArrayList<byte[]> hashes = new ArrayList<byte[]>(sigs.length);
43751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        for (Signature s : sigs) {
43851fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            hashes.add(BackupManagerService.hashSignature(s));
43951fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        }
44051fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        return hashes;
44151fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    }
44251fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
44351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    private static void writeSignatureHashArray(DataOutputStream out, ArrayList<byte[]> hashes)
444e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown            throws IOException {
44551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        // the number of entries in the array
44651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        out.writeInt(hashes.size());
44751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
44851fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        // the hash arrays themselves as length + contents
44951fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        for (byte[] buffer : hashes) {
45051fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            out.writeInt(buffer.length);
45151fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            out.write(buffer);
4526785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        }
4536785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    }
4546785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
45551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    private static ArrayList<byte[]> readSignatureHashArray(DataInputStream in) {
4566785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        try {
457e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown            int num;
458e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown            try {
459e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                num = in.readInt();
460e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown            } catch (EOFException e) {
461e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                // clean termination
462e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                Slog.w(TAG, "Read empty signature block");
463e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown                return null;
464e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown            }
465e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown
4668a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            if (DEBUG) Slog.v(TAG, " ... unflatten read " + num);
467e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown
4685a8a1151e267b29978f219f9569fdfc5e74cc210Christopher Tate            // Sensical?
4695a8a1151e267b29978f219f9569fdfc5e74cc210Christopher Tate            if (num > 20) {
4708a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                Slog.e(TAG, "Suspiciously large sig count in restore data; aborting");
4715a8a1151e267b29978f219f9569fdfc5e74cc210Christopher Tate                throw new IllegalStateException("Bad restore state");
4725a8a1151e267b29978f219f9569fdfc5e74cc210Christopher Tate            }
47351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
47451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            // This could be a "legacy" block of actual signatures rather than their hashes.
47551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            // If this is the case, convert them now.  We judge based on the payload size:
47651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            // if the blocks are all 256 bits (32 bytes) then we take them to be SHA-256 hashes;
47751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            // otherwise we take them to be Signatures.
47851fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            boolean nonHashFound = false;
47951fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            ArrayList<byte[]> sigs = new ArrayList<byte[]>(num);
4806785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            for (int i = 0; i < num; i++) {
4816785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate                int len = in.readInt();
48251fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                byte[] readHash = new byte[len];
48351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                in.read(readHash);
48451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                sigs.add(readHash);
48551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                if (len != 32) {
48651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                    nonHashFound = true;
48751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                }
4886785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            }
48951fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
49051fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            if (nonHashFound) {
49151fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                ArrayList<byte[]> hashes =
49251fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                        new ArrayList<byte[]>(sigs.size());
49351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                for (int i = 0; i < sigs.size(); i++) {
49451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                    Signature s = new Signature(sigs.get(i));
49551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                    hashes.add(BackupManagerService.hashSignature(s));
49651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                }
49751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                sigs = hashes;
49851fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            }
49951fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
500e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown            return sigs;
5016785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        } catch (IOException e) {
502e684d9582cedf9bd5cc6c6fe47c600a79a13d816Jeff Brown            Slog.e(TAG, "Unable to read signatures");
5036785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            return null;
5046785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        }
5056785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    }
5066785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
5076785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    // Util: parse out an existing state file into a usable structure
50872d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate    private void parseStateFile(ParcelFileDescriptor stateFile) {
50972d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate        mExisting.clear();
51072d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate        mStateVersions.clear();
51172d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate        mStoredSdkVersion = 0;
51272d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate        mStoredIncrementalVersion = null;
513a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        mStoredHomeComponent = null;
514a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        mStoredHomeVersion = 0;
51551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        mStoredHomeSigHashes = null;
51672d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate
5176785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        // The state file is just the list of app names we have stored signatures for
51872d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate        // with the exception of the metadata block, to which is also appended the
51972d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate        // version numbers corresponding with the last time we wrote this PM block.
52072d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate        // If they mismatch the current system, we'll re-store the metadata key.
5216785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor());
522a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        BufferedInputStream inbuffer = new BufferedInputStream(instream);
523a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        DataInputStream in = new DataInputStream(inbuffer);
5246785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
5256785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        try {
52651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            boolean ignoreExisting = false;
52772d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate            String pkg = in.readUTF();
528a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate
52951fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            // Validate the state file version is sensical to us
53051fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            if (pkg.equals(STATE_FILE_HEADER)) {
53151fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                int stateVersion = in.readInt();
53251fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                if (stateVersion > STATE_FILE_VERSION) {
53351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                    Slog.w(TAG, "Unsupported state file version " + stateVersion
53451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                            + ", redoing from start");
53551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                    return;
53651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                }
53722192ada99691221f9ab167c046fb71c23baf723Christopher Tate                pkg = in.readUTF();
53851fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            } else {
53951fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                // This is an older version of the state file in which the lead element
54051fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                // is not a STATE_FILE_VERSION string.  If that's the case, we want to
54151fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                // make sure to write our full backup dataset when given an opportunity.
54251fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                // We trigger that by simply not marking the restored package metadata
54351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                // as known-to-exist-in-archive.
54451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                Slog.i(TAG, "Older version of saved state - rewriting");
54551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                ignoreExisting = true;
54651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            }
54751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
548a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            // First comes the preferred home app data, if any, headed by the DEFAULT_HOME_KEY tag
549a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            if (pkg.equals(DEFAULT_HOME_KEY)) {
550a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                // flattened component name, version, signature of the home app
551a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                mStoredHomeComponent = ComponentName.unflattenFromString(in.readUTF());
552a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                mStoredHomeVersion = in.readLong();
55351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                mStoredHomeSigHashes = readSignatureHashArray(in);
554a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate
555a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                pkg = in.readUTF(); // set up for the next block of state
556a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            } else {
557a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                // else no preferred home app on the ancestral device - fall through to the rest
558a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            }
559a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate
560a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            // After (possible) home app data comes the global metadata block
56172d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate            if (pkg.equals(GLOBAL_METADATA_KEY)) {
56272d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                mStoredSdkVersion = in.readInt();
56372d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                mStoredIncrementalVersion = in.readUTF();
56451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                if (!ignoreExisting) {
56551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                    mExisting.add(GLOBAL_METADATA_KEY);
56651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                }
56772d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate            } else {
5688a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                Slog.e(TAG, "No global metadata in state file!");
56972d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                return;
57072d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate            }
57172d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate
572a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            // The global metadata was last; now read all the apps
57372d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate            while (true) {
57472d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                pkg = in.readUTF();
57572d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                int versionCode = in.readInt();
57651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
57751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                if (!ignoreExisting) {
57851fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                    mExisting.add(pkg);
57951fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                }
58072d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                mStateVersions.put(pkg, new Metadata(versionCode, null));
5816785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            }
5826785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        } catch (EOFException eof) {
5836785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            // safe; we're done
5846785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        } catch (IOException e) {
5856785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            // whoops, bad state file.  abort.
5868a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            Slog.e(TAG, "Unable to read Package Manager state file: " + e);
5876785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        }
5886785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    }
5896785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
590a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate    private ComponentName getPreferredHomeComponent() {
591a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        return mPackageManager.getHomeActivities(new ArrayList<ResolveInfo>());
592a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate    }
593a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate
5943a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate    // Util: write out our new backup state file
595a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate    private void writeStateFile(List<PackageInfo> pkgs, ComponentName preferredHome,
59651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            long homeVersion, ArrayList<byte[]> homeSigHashes, ParcelFileDescriptor stateFile) {
5976785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
598a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        BufferedOutputStream outbuf = new BufferedOutputStream(outstream);
599a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        DataOutputStream out = new DataOutputStream(outbuf);
6006785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate
601a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate        // by the time we get here we know we've done all our backing up
6023a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate        try {
60351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            // state file version header
60451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            out.writeUTF(STATE_FILE_HEADER);
60551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            out.writeInt(STATE_FILE_VERSION);
60651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate
607a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            // If we remembered a preferred home app, record that
608a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            if (preferredHome != null) {
609a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                out.writeUTF(DEFAULT_HOME_KEY);
610a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                out.writeUTF(preferredHome.flattenToString());
611a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate                out.writeLong(homeVersion);
61251fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                writeSignatureHashArray(out, homeSigHashes);
613a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            }
614a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate
615a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            // Conclude with the metadata block
61672d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate            out.writeUTF(GLOBAL_METADATA_KEY);
61772d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate            out.writeInt(Build.VERSION.SDK_INT);
61872d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate            out.writeUTF(Build.VERSION.INCREMENTAL);
6193a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate
62051fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate            // now write all the app names + versions
621efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            for (PackageInfo pkg : pkgs) {
62272d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                out.writeUTF(pkg.packageName);
62372d19aa51e90d45c7895629db78e548da2f6d469Christopher Tate                out.writeInt(pkg.versionCode);
6246785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate            }
625a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate
626a99d02170bc0e54f729b2b735571c8eea8d5034dChristopher Tate            out.flush();
6273a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate        } catch (IOException e) {
6288a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            Slog.e(TAG, "Unable to write package manager state file!");
6296785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate        }
6306785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate    }
6316785dd842075889e5230d93ed9c0ab9c204ab432Christopher Tate}
632