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