| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2009 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.android.server; |
| 18 | |
| 19 | import android.app.BackupAgent; |
| 20 | import android.backup.BackupDataInput; |
| 21 | import android.backup.BackupDataOutput; |
| 22 | import android.content.pm.ApplicationInfo; |
| 23 | import android.content.pm.PackageInfo; |
| 24 | import android.content.pm.PackageManager; |
| 25 | import android.content.pm.PackageManager.NameNotFoundException; |
| 26 | import android.content.pm.Signature; |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 27 | import android.os.Build; |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 28 | import android.os.ParcelFileDescriptor; |
| 29 | import android.util.Log; |
| 30 | |
| 31 | import java.io.ByteArrayInputStream; |
| 32 | import java.io.ByteArrayOutputStream; |
| 33 | import java.io.DataInputStream; |
| 34 | import java.io.DataOutputStream; |
| 35 | import java.io.EOFException; |
| 36 | import java.io.FileInputStream; |
| 37 | import java.io.FileOutputStream; |
| 38 | import java.io.IOException; |
| 39 | import java.util.ArrayList; |
| 40 | import java.util.HashMap; |
| 41 | import java.util.HashSet; |
| 42 | import java.util.List; |
| 43 | |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 44 | /** |
| 45 | * We back up the signatures of each package so that during a system restore, |
| 46 | * we can verify that the app whose data we think we have matches the app |
| 47 | * actually resident on the device. |
| 48 | * |
| 49 | * Since the Package Manager isn't a proper "application" we just provide a |
| 50 | * direct IBackupAgent implementation and hand-construct it at need. |
| 51 | */ |
| 52 | public class PackageManagerBackupAgent extends BackupAgent { |
| 53 | private static final String TAG = "PMBA"; |
| 54 | private static final boolean DEBUG = true; |
| 55 | |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 56 | // key under which we store global metadata (individual app metadata |
| 57 | // is stored using the package name as a key) |
| 58 | private static final String GLOBAL_METADATA_KEY = "@meta@"; |
| 59 | |
| Dan Egnor | efe5264 | 2009-06-24 00:16:33 -0700 | [diff] [blame] | 60 | private List<PackageInfo> mAllPackages; |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 61 | private PackageManager mPackageManager; |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 62 | // version & signature info of each app in a restore set |
| Christopher Tate | 6aa41f4 | 2009-06-19 14:14:22 -0700 | [diff] [blame] | 63 | private HashMap<String, Metadata> mRestoredSignatures; |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 64 | // The version info of each backed-up app as read from the state file |
| 65 | private HashMap<String, Metadata> mStateVersions = new HashMap<String, Metadata>(); |
| 66 | |
| 67 | private final HashSet<String> mExisting = new HashSet<String>(); |
| 68 | private int mStoredSdkVersion; |
| 69 | private String mStoredIncrementalVersion; |
| Christopher Tate | 6aa41f4 | 2009-06-19 14:14:22 -0700 | [diff] [blame] | 70 | |
| 71 | public class Metadata { |
| 72 | public int versionCode; |
| 73 | public Signature[] signatures; |
| 74 | |
| 75 | Metadata(int version, Signature[] sigs) { |
| 76 | versionCode = version; |
| 77 | signatures = sigs; |
| 78 | } |
| 79 | } |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 80 | |
| 81 | // We're constructed with the set of applications that are participating |
| 82 | // in backup. This set changes as apps are installed & removed. |
| Dan Egnor | efe5264 | 2009-06-24 00:16:33 -0700 | [diff] [blame] | 83 | PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) { |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 84 | mPackageManager = packageMgr; |
| Dan Egnor | efe5264 | 2009-06-24 00:16:33 -0700 | [diff] [blame] | 85 | mAllPackages = packages; |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 86 | mRestoredSignatures = null; |
| 87 | } |
| 88 | |
| Christopher Tate | 6aa41f4 | 2009-06-19 14:14:22 -0700 | [diff] [blame] | 89 | public Metadata getRestoredMetadata(String packageName) { |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 90 | if (mRestoredSignatures == null) { |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 91 | Log.w(TAG, "getRestoredMetadata() before metadata read!"); |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 92 | return null; |
| 93 | } |
| 94 | |
| 95 | return mRestoredSignatures.get(packageName); |
| 96 | } |
| 97 | |
| 98 | // The backed up data is the signature block for each app, keyed by |
| 99 | // the package name. |
| 100 | public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, |
| 101 | ParcelFileDescriptor newState) { |
| Christopher Tate | 6aa41f4 | 2009-06-19 14:14:22 -0700 | [diff] [blame] | 102 | if (DEBUG) Log.v(TAG, "onBackup()"); |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 103 | |
| Christopher Tate | 6aa41f4 | 2009-06-19 14:14:22 -0700 | [diff] [blame] | 104 | ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); // we'll reuse these |
| 105 | DataOutputStream outWriter = new DataOutputStream(bufStream); |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 106 | parseStateFile(oldState); |
| 107 | |
| 108 | // If the stored version string differs, we need to re-backup all |
| 109 | // of the metadata. We force this by removing everything from the |
| 110 | // "already backed up" map built by parseStateFile(). |
| 111 | if (mStoredIncrementalVersion == null |
| 112 | || !mStoredIncrementalVersion.equals(Build.VERSION.INCREMENTAL)) { |
| 113 | Log.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs " |
| 114 | + Build.VERSION.INCREMENTAL + " - rewriting"); |
| 115 | mExisting.clear(); |
| 116 | } |
| Christopher Tate | 6aa41f4 | 2009-06-19 14:14:22 -0700 | [diff] [blame] | 117 | |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 118 | try { |
| 119 | /* |
| 120 | * Global metadata: |
| 121 | * |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 122 | * int SDKversion -- the SDK version of the OS itself on the device |
| 123 | * that produced this backup set. Used to reject |
| 124 | * backups from later OSes onto earlier ones. |
| 125 | * String incremental -- the incremental release name of the OS stored in |
| 126 | * the backup set. |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 127 | */ |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 128 | if (!mExisting.contains(GLOBAL_METADATA_KEY)) { |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 129 | if (DEBUG) Log.v(TAG, "Storing global metadata key"); |
| 130 | outWriter.writeInt(Build.VERSION.SDK_INT); |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 131 | outWriter.writeUTF(Build.VERSION.INCREMENTAL); |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 132 | byte[] metadata = bufStream.toByteArray(); |
| 133 | data.writeEntityHeader(GLOBAL_METADATA_KEY, metadata.length); |
| 134 | data.writeEntityData(metadata, metadata.length); |
| 135 | } else { |
| 136 | if (DEBUG) Log.v(TAG, "Global metadata key already stored"); |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 137 | // don't consider it to have been skipped/deleted |
| 138 | mExisting.remove(GLOBAL_METADATA_KEY); |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 139 | } |
| Christopher Tate | 6aa41f4 | 2009-06-19 14:14:22 -0700 | [diff] [blame] | 140 | |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 141 | // For each app we have on device, see if we've backed it up yet. If not, |
| 142 | // write its signature block to the output, keyed on the package name. |
| Dan Egnor | efe5264 | 2009-06-24 00:16:33 -0700 | [diff] [blame] | 143 | for (PackageInfo pkg : mAllPackages) { |
| 144 | String packName = pkg.packageName; |
| Christopher Tate | 6f31742 | 2009-06-29 18:52:55 -0700 | [diff] [blame] | 145 | if (packName.equals(GLOBAL_METADATA_KEY)) { |
| 146 | // We've already handled the metadata key; skip it here |
| 147 | continue; |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 148 | } else { |
| 149 | PackageInfo info = null; |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 150 | try { |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 151 | info = mPackageManager.getPackageInfo(packName, |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 152 | PackageManager.GET_SIGNATURES); |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 153 | } catch (NameNotFoundException e) { |
| 154 | // Weird; we just found it, and now are told it doesn't exist. |
| 155 | // Treat it as having been removed from the device. |
| 156 | mExisting.add(packName); |
| 157 | continue; |
| 158 | } |
| 159 | |
| 160 | boolean doBackup = false; |
| 161 | if (!mExisting.contains(packName)) { |
| 162 | // We haven't backed up this app before |
| 163 | doBackup = true; |
| 164 | } else { |
| 165 | // We *have* backed this one up before. Check whether the version |
| 166 | // of the backup matches the version of the current app; if they |
| 167 | // don't match, the app has been updated and we need to store its |
| 168 | // metadata again. In either case, take it out of mExisting so that |
| 169 | // we don't consider it deleted later. |
| 170 | if (info.versionCode != mStateVersions.get(packName).versionCode) { |
| 171 | doBackup = true; |
| 172 | } |
| 173 | mExisting.remove(packName); |
| 174 | } |
| 175 | |
| 176 | if (doBackup) { |
| 177 | // We need to store this app's metadata |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 178 | /* |
| 179 | * Metadata for each package: |
| 180 | * |
| 181 | * int version -- [4] the package's versionCode |
| 182 | * byte[] signatures -- [len] flattened Signature[] of the package |
| 183 | */ |
| Christopher Tate | 6aa41f4 | 2009-06-19 14:14:22 -0700 | [diff] [blame] | 184 | |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 185 | // marshal the version code in a canonical form |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 186 | bufStream.reset(); |
| 187 | outWriter.writeInt(info.versionCode); |
| 188 | byte[] versionBuf = bufStream.toByteArray(); |
| 189 | |
| 190 | byte[] sigs = flattenSignatureArray(info.signatures); |
| 191 | |
| 192 | // !!! TODO: take out this debugging |
| 193 | if (DEBUG) { |
| 194 | Log.v(TAG, "+ metadata for " + packName |
| 195 | + " version=" + info.versionCode |
| 196 | + " versionLen=" + versionBuf.length |
| 197 | + " sigsLen=" + sigs.length); |
| 198 | } |
| 199 | // Now we can write the backup entity for this package |
| 200 | data.writeEntityHeader(packName, versionBuf.length + sigs.length); |
| 201 | data.writeEntityData(versionBuf, versionBuf.length); |
| 202 | data.writeEntityData(sigs, sigs.length); |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 203 | } |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | // At this point, the only entries in 'existing' are apps that were |
| 208 | // mentioned in the saved state file, but appear to no longer be present |
| 209 | // on the device. Write a deletion entity for them. |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 210 | for (String app : mExisting) { |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 211 | // !!! TODO: take out this msg |
| 212 | if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app); |
| 213 | try { |
| 214 | data.writeEntityHeader(app, -1); |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 215 | } catch (IOException e) { |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 216 | Log.e(TAG, "Unable to write package deletions!"); |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 217 | return; |
| 218 | } |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 219 | } |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 220 | } catch (IOException e) { |
| 221 | // Real error writing data |
| 222 | Log.e(TAG, "Unable to write package backup data file!"); |
| 223 | return; |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 224 | } |
| 225 | |
| 226 | // Finally, write the new state blob -- just the list of all apps we handled |
| Dan Egnor | efe5264 | 2009-06-24 00:16:33 -0700 | [diff] [blame] | 227 | writeStateFile(mAllPackages, newState); |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 228 | } |
| 229 | |
| 230 | // "Restore" here is a misnomer. What we're really doing is reading back the |
| 231 | // set of app signatures associated with each backed-up app in this restore |
| 232 | // image. We'll use those later to determine what we can legitimately restore. |
| Christopher Tate | 5cbbf56 | 2009-06-22 16:44:51 -0700 | [diff] [blame] | 233 | public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 234 | throws IOException { |
| 235 | List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>(); |
| Christopher Tate | 6aa41f4 | 2009-06-19 14:14:22 -0700 | [diff] [blame] | 236 | HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>(); |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 237 | if (DEBUG) Log.v(TAG, "onRestore()"); |
| 238 | int storedSystemVersion = -1; |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 239 | |
| 240 | while (data.readNextHeader()) { |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 241 | String key = data.getKey(); |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 242 | int dataSize = data.getDataSize(); |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 243 | |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 244 | if (DEBUG) Log.v(TAG, " got key=" + key + " dataSize=" + dataSize); |
| Christopher Tate | 6aa41f4 | 2009-06-19 14:14:22 -0700 | [diff] [blame] | 245 | |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 246 | // generic setup to parse any entity data |
| 247 | byte[] dataBuf = new byte[dataSize]; |
| 248 | data.readEntityData(dataBuf, 0, dataSize); |
| 249 | ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf); |
| 250 | DataInputStream in = new DataInputStream(baStream); |
| 251 | |
| 252 | if (key.equals(GLOBAL_METADATA_KEY)) { |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 253 | int storedSdkVersion = in.readInt(); |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 254 | if (DEBUG) Log.v(TAG, " storedSystemVersion = " + storedSystemVersion); |
| 255 | if (storedSystemVersion > Build.VERSION.SDK_INT) { |
| 256 | // returning before setting the sig map means we rejected the restore set |
| 257 | Log.w(TAG, "Restore set was from a later version of Android; not restoring"); |
| 258 | return; |
| 259 | } |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 260 | mStoredSdkVersion = storedSdkVersion; |
| 261 | mStoredIncrementalVersion = in.readUTF(); |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 262 | // !!! TODO: remove this debugging output |
| 263 | if (DEBUG) { |
| 264 | Log.i(TAG, "Restore set version " + storedSystemVersion |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 265 | + " is compatible with OS version " + Build.VERSION.SDK_INT |
| 266 | + " (" + mStoredIncrementalVersion + " vs " |
| 267 | + Build.VERSION.INCREMENTAL + ")"); |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 268 | } |
| 269 | } else { |
| 270 | // it's a file metadata record |
| 271 | int versionCode = in.readInt(); |
| 272 | Signature[] sigs = unflattenSignatureArray(in); |
| 273 | // !!! TODO: take out this debugging |
| 274 | if (DEBUG) { |
| 275 | Log.i(TAG, " restored metadata for " + key |
| 276 | + " dataSize=" + dataSize |
| 277 | + " versionCode=" + versionCode + " sigs=" + sigs); |
| 278 | } |
| 279 | |
| 280 | ApplicationInfo app = new ApplicationInfo(); |
| 281 | app.packageName = key; |
| 282 | restoredApps.add(app); |
| 283 | sigMap.put(key, new Metadata(versionCode, sigs)); |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 284 | } |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 285 | } |
| 286 | |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 287 | // On successful completion, cache the signature map for the Backup Manager to use |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 288 | mRestoredSignatures = sigMap; |
| 289 | } |
| 290 | |
| 291 | |
| 292 | // Util: convert an array of Signatures into a flattened byte buffer. The |
| 293 | // flattened format contains enough info to reconstruct the signature array. |
| 294 | private byte[] flattenSignatureArray(Signature[] allSigs) { |
| 295 | ByteArrayOutputStream outBuf = new ByteArrayOutputStream(); |
| 296 | DataOutputStream out = new DataOutputStream(outBuf); |
| 297 | |
| 298 | // build the set of subsidiary buffers |
| 299 | try { |
| 300 | // first the # of signatures in the array |
| 301 | out.writeInt(allSigs.length); |
| 302 | |
| 303 | // then the signatures themselves, length + flattened buffer |
| 304 | for (Signature sig : allSigs) { |
| 305 | byte[] flat = sig.toByteArray(); |
| 306 | out.writeInt(flat.length); |
| 307 | out.write(flat); |
| 308 | } |
| 309 | } catch (IOException e) { |
| 310 | // very strange; we're writing to memory here. abort. |
| 311 | return null; |
| 312 | } |
| 313 | |
| 314 | return outBuf.toByteArray(); |
| 315 | } |
| 316 | |
| Christopher Tate | 6aa41f4 | 2009-06-19 14:14:22 -0700 | [diff] [blame] | 317 | private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) { |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 318 | Signature[] sigs = null; |
| 319 | |
| 320 | try { |
| 321 | int num = in.readInt(); |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 322 | Log.v(TAG, " ... unflatten read " + num); |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 323 | sigs = new Signature[num]; |
| 324 | for (int i = 0; i < num; i++) { |
| 325 | int len = in.readInt(); |
| 326 | byte[] flatSig = new byte[len]; |
| 327 | in.read(flatSig); |
| 328 | sigs[i] = new Signature(flatSig); |
| 329 | } |
| 330 | } catch (EOFException e) { |
| 331 | // clean termination |
| 332 | if (sigs == null) { |
| 333 | Log.w(TAG, "Empty signature block found"); |
| 334 | } |
| 335 | } catch (IOException e) { |
| 336 | Log.d(TAG, "Unable to unflatten sigs"); |
| 337 | return null; |
| 338 | } |
| 339 | |
| 340 | return sigs; |
| 341 | } |
| 342 | |
| 343 | // Util: parse out an existing state file into a usable structure |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 344 | private void parseStateFile(ParcelFileDescriptor stateFile) { |
| 345 | mExisting.clear(); |
| 346 | mStateVersions.clear(); |
| 347 | mStoredSdkVersion = 0; |
| 348 | mStoredIncrementalVersion = null; |
| 349 | |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 350 | // The state file is just the list of app names we have stored signatures for |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 351 | // with the exception of the metadata block, to which is also appended the |
| 352 | // version numbers corresponding with the last time we wrote this PM block. |
| 353 | // If they mismatch the current system, we'll re-store the metadata key. |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 354 | FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor()); |
| 355 | DataInputStream in = new DataInputStream(instream); |
| 356 | |
| 357 | int bufSize = 256; |
| 358 | byte[] buf = new byte[bufSize]; |
| 359 | try { |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 360 | String pkg = in.readUTF(); |
| 361 | if (pkg.equals(GLOBAL_METADATA_KEY)) { |
| 362 | mStoredSdkVersion = in.readInt(); |
| 363 | mStoredIncrementalVersion = in.readUTF(); |
| 364 | mExisting.add(GLOBAL_METADATA_KEY); |
| 365 | } else { |
| 366 | Log.e(TAG, "No global metadata in state file!"); |
| 367 | return; |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 368 | } |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 369 | |
| 370 | // The global metadata was first; now read all the apps |
| 371 | while (true) { |
| 372 | pkg = in.readUTF(); |
| 373 | int versionCode = in.readInt(); |
| 374 | mExisting.add(pkg); |
| 375 | mStateVersions.put(pkg, new Metadata(versionCode, null)); |
| 376 | } |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 377 | } catch (EOFException eof) { |
| 378 | // safe; we're done |
| 379 | } catch (IOException e) { |
| 380 | // whoops, bad state file. abort. |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 381 | Log.e(TAG, "Unable to read Package Manager state file: " + e); |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 382 | } |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 383 | } |
| 384 | |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 385 | // Util: write out our new backup state file |
| Dan Egnor | efe5264 | 2009-06-24 00:16:33 -0700 | [diff] [blame] | 386 | private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) { |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 387 | FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); |
| 388 | DataOutputStream out = new DataOutputStream(outstream); |
| 389 | |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 390 | try { |
| 391 | // by the time we get here we know we've stored the global metadata record |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 392 | out.writeUTF(GLOBAL_METADATA_KEY); |
| 393 | out.writeInt(Build.VERSION.SDK_INT); |
| 394 | out.writeUTF(Build.VERSION.INCREMENTAL); |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 395 | |
| 396 | // now write all the app names too |
| Dan Egnor | efe5264 | 2009-06-24 00:16:33 -0700 | [diff] [blame] | 397 | for (PackageInfo pkg : pkgs) { |
| Christopher Tate | 72d19aa | 2009-06-30 12:47:33 -0700 | [diff] [blame] | 398 | out.writeUTF(pkg.packageName); |
| 399 | out.writeInt(pkg.versionCode); |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 400 | } |
| Christopher Tate | 3a31a93 | 2009-06-22 15:10:30 -0700 | [diff] [blame] | 401 | } catch (IOException e) { |
| 402 | Log.e(TAG, "Unable to write package manager state file!"); |
| 403 | return; |
| Christopher Tate | 6785dd8 | 2009-06-18 15:58:25 -0700 | [diff] [blame] | 404 | } |
| 405 | } |
| 406 | } |