blob: dbd18264af08bf57f68824a6afcfb09abd362603 [file] [log] [blame]
Christopher Tate6785dd82009-06-18 15:58:25 -07001/*
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
17package com.android.server;
18
19import android.app.BackupAgent;
20import android.backup.BackupDataInput;
21import android.backup.BackupDataOutput;
22import android.content.pm.ApplicationInfo;
23import android.content.pm.PackageInfo;
24import android.content.pm.PackageManager;
25import android.content.pm.PackageManager.NameNotFoundException;
26import android.content.pm.Signature;
Christopher Tate3a31a932009-06-22 15:10:30 -070027import android.os.Build;
Christopher Tate6785dd82009-06-18 15:58:25 -070028import android.os.ParcelFileDescriptor;
29import android.util.Log;
30
31import java.io.ByteArrayInputStream;
32import java.io.ByteArrayOutputStream;
33import java.io.DataInputStream;
34import java.io.DataOutputStream;
35import java.io.EOFException;
36import java.io.FileInputStream;
37import java.io.FileOutputStream;
38import java.io.IOException;
39import java.util.ArrayList;
40import java.util.HashMap;
41import java.util.HashSet;
42import java.util.List;
43
Christopher Tate6785dd82009-06-18 15:58:25 -070044/**
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 */
52public class PackageManagerBackupAgent extends BackupAgent {
53 private static final String TAG = "PMBA";
Christopher Tateb808a9392009-09-29 16:09:52 -070054 private static final boolean DEBUG = false;
Christopher Tate6785dd82009-06-18 15:58:25 -070055
Christopher Tate3a31a932009-06-22 15:10:30 -070056 // 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 Egnorefe52642009-06-24 00:16:33 -070060 private List<PackageInfo> mAllPackages;
Christopher Tate6785dd82009-06-18 15:58:25 -070061 private PackageManager mPackageManager;
Christopher Tate72d19aa2009-06-30 12:47:33 -070062 // version & signature info of each app in a restore set
Christopher Tate6aa41f42009-06-19 14:14:22 -070063 private HashMap<String, Metadata> mRestoredSignatures;
Christopher Tate72d19aa2009-06-30 12:47:33 -070064 // 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 Tate3d7cd132009-07-07 14:23:07 -070070 private boolean mHasMetadata;
Christopher Tate6aa41f42009-06-19 14:14:22 -070071
72 public class Metadata {
73 public int versionCode;
74 public Signature[] signatures;
75
76 Metadata(int version, Signature[] sigs) {
77 versionCode = version;
78 signatures = sigs;
79 }
80 }
Christopher Tate6785dd82009-06-18 15:58:25 -070081
82 // We're constructed with the set of applications that are participating
83 // in backup. This set changes as apps are installed & removed.
Dan Egnorefe52642009-06-24 00:16:33 -070084 PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
Christopher Tate6785dd82009-06-18 15:58:25 -070085 mPackageManager = packageMgr;
Dan Egnorefe52642009-06-24 00:16:33 -070086 mAllPackages = packages;
Christopher Tate6785dd82009-06-18 15:58:25 -070087 mRestoredSignatures = null;
Christopher Tate3d7cd132009-07-07 14:23:07 -070088 mHasMetadata = false;
89 }
90
91 public boolean hasMetadata() {
92 return mHasMetadata;
Christopher Tate6785dd82009-06-18 15:58:25 -070093 }
94
Christopher Tate6aa41f42009-06-19 14:14:22 -070095 public Metadata getRestoredMetadata(String packageName) {
Christopher Tate6785dd82009-06-18 15:58:25 -070096 if (mRestoredSignatures == null) {
Christopher Tate3a31a932009-06-22 15:10:30 -070097 Log.w(TAG, "getRestoredMetadata() before metadata read!");
Christopher Tate6785dd82009-06-18 15:58:25 -070098 return null;
99 }
100
101 return mRestoredSignatures.get(packageName);
102 }
103
104 // The backed up data is the signature block for each app, keyed by
105 // the package name.
106 public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
107 ParcelFileDescriptor newState) {
Christopher Tate6aa41f42009-06-19 14:14:22 -0700108 if (DEBUG) Log.v(TAG, "onBackup()");
Christopher Tate3a31a932009-06-22 15:10:30 -0700109
Christopher Tate6aa41f42009-06-19 14:14:22 -0700110 ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); // we'll reuse these
111 DataOutputStream outWriter = new DataOutputStream(bufStream);
Christopher Tate72d19aa2009-06-30 12:47:33 -0700112 parseStateFile(oldState);
113
114 // If the stored version string differs, we need to re-backup all
115 // of the metadata. We force this by removing everything from the
116 // "already backed up" map built by parseStateFile().
117 if (mStoredIncrementalVersion == null
118 || !mStoredIncrementalVersion.equals(Build.VERSION.INCREMENTAL)) {
119 Log.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs "
120 + Build.VERSION.INCREMENTAL + " - rewriting");
121 mExisting.clear();
122 }
Christopher Tate6aa41f42009-06-19 14:14:22 -0700123
Christopher Tate3a31a932009-06-22 15:10:30 -0700124 try {
125 /*
126 * Global metadata:
127 *
Christopher Tate72d19aa2009-06-30 12:47:33 -0700128 * int SDKversion -- the SDK version of the OS itself on the device
129 * that produced this backup set. Used to reject
130 * backups from later OSes onto earlier ones.
131 * String incremental -- the incremental release name of the OS stored in
132 * the backup set.
Christopher Tate3a31a932009-06-22 15:10:30 -0700133 */
Christopher Tate72d19aa2009-06-30 12:47:33 -0700134 if (!mExisting.contains(GLOBAL_METADATA_KEY)) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700135 if (DEBUG) Log.v(TAG, "Storing global metadata key");
136 outWriter.writeInt(Build.VERSION.SDK_INT);
Christopher Tate72d19aa2009-06-30 12:47:33 -0700137 outWriter.writeUTF(Build.VERSION.INCREMENTAL);
Christopher Tate3a31a932009-06-22 15:10:30 -0700138 byte[] metadata = bufStream.toByteArray();
139 data.writeEntityHeader(GLOBAL_METADATA_KEY, metadata.length);
140 data.writeEntityData(metadata, metadata.length);
141 } else {
142 if (DEBUG) Log.v(TAG, "Global metadata key already stored");
Christopher Tate72d19aa2009-06-30 12:47:33 -0700143 // don't consider it to have been skipped/deleted
144 mExisting.remove(GLOBAL_METADATA_KEY);
Christopher Tate3a31a932009-06-22 15:10:30 -0700145 }
Christopher Tate6aa41f42009-06-19 14:14:22 -0700146
Christopher Tate3a31a932009-06-22 15:10:30 -0700147 // For each app we have on device, see if we've backed it up yet. If not,
148 // write its signature block to the output, keyed on the package name.
Dan Egnorefe52642009-06-24 00:16:33 -0700149 for (PackageInfo pkg : mAllPackages) {
150 String packName = pkg.packageName;
Christopher Tate6f317422009-06-29 18:52:55 -0700151 if (packName.equals(GLOBAL_METADATA_KEY)) {
152 // We've already handled the metadata key; skip it here
153 continue;
Christopher Tate72d19aa2009-06-30 12:47:33 -0700154 } else {
155 PackageInfo info = null;
Christopher Tate3a31a932009-06-22 15:10:30 -0700156 try {
Christopher Tate72d19aa2009-06-30 12:47:33 -0700157 info = mPackageManager.getPackageInfo(packName,
Christopher Tate3a31a932009-06-22 15:10:30 -0700158 PackageManager.GET_SIGNATURES);
Christopher Tate72d19aa2009-06-30 12:47:33 -0700159 } catch (NameNotFoundException e) {
160 // Weird; we just found it, and now are told it doesn't exist.
161 // Treat it as having been removed from the device.
162 mExisting.add(packName);
163 continue;
164 }
165
166 boolean doBackup = false;
167 if (!mExisting.contains(packName)) {
168 // We haven't backed up this app before
169 doBackup = true;
170 } else {
171 // We *have* backed this one up before. Check whether the version
172 // of the backup matches the version of the current app; if they
173 // don't match, the app has been updated and we need to store its
174 // metadata again. In either case, take it out of mExisting so that
175 // we don't consider it deleted later.
176 if (info.versionCode != mStateVersions.get(packName).versionCode) {
177 doBackup = true;
178 }
179 mExisting.remove(packName);
180 }
181
182 if (doBackup) {
183 // We need to store this app's metadata
Christopher Tate3a31a932009-06-22 15:10:30 -0700184 /*
185 * Metadata for each package:
186 *
187 * int version -- [4] the package's versionCode
188 * byte[] signatures -- [len] flattened Signature[] of the package
189 */
Christopher Tate6aa41f42009-06-19 14:14:22 -0700190
Christopher Tate72d19aa2009-06-30 12:47:33 -0700191 // marshal the version code in a canonical form
Christopher Tate3a31a932009-06-22 15:10:30 -0700192 bufStream.reset();
193 outWriter.writeInt(info.versionCode);
194 byte[] versionBuf = bufStream.toByteArray();
195
196 byte[] sigs = flattenSignatureArray(info.signatures);
197
Christopher Tate3a31a932009-06-22 15:10:30 -0700198 if (DEBUG) {
199 Log.v(TAG, "+ metadata for " + packName
200 + " version=" + info.versionCode
201 + " versionLen=" + versionBuf.length
202 + " sigsLen=" + sigs.length);
203 }
204 // Now we can write the backup entity for this package
205 data.writeEntityHeader(packName, versionBuf.length + sigs.length);
206 data.writeEntityData(versionBuf, versionBuf.length);
207 data.writeEntityData(sigs, sigs.length);
Christopher Tate3a31a932009-06-22 15:10:30 -0700208 }
209 }
210 }
211
212 // At this point, the only entries in 'existing' are apps that were
213 // mentioned in the saved state file, but appear to no longer be present
214 // on the device. Write a deletion entity for them.
Christopher Tate72d19aa2009-06-30 12:47:33 -0700215 for (String app : mExisting) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700216 if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app);
217 try {
218 data.writeEntityHeader(app, -1);
Christopher Tate6785dd82009-06-18 15:58:25 -0700219 } catch (IOException e) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700220 Log.e(TAG, "Unable to write package deletions!");
Christopher Tate6785dd82009-06-18 15:58:25 -0700221 return;
222 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700223 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700224 } catch (IOException e) {
225 // Real error writing data
226 Log.e(TAG, "Unable to write package backup data file!");
227 return;
Christopher Tate6785dd82009-06-18 15:58:25 -0700228 }
229
230 // Finally, write the new state blob -- just the list of all apps we handled
Dan Egnorefe52642009-06-24 00:16:33 -0700231 writeStateFile(mAllPackages, newState);
Christopher Tate6785dd82009-06-18 15:58:25 -0700232 }
233
234 // "Restore" here is a misnomer. What we're really doing is reading back the
235 // set of app signatures associated with each backed-up app in this restore
236 // image. We'll use those later to determine what we can legitimately restore.
Christopher Tate5cbbf562009-06-22 16:44:51 -0700237 public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
Christopher Tate6785dd82009-06-18 15:58:25 -0700238 throws IOException {
239 List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
Christopher Tate6aa41f42009-06-19 14:14:22 -0700240 HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>();
Christopher Tate3a31a932009-06-22 15:10:30 -0700241 if (DEBUG) Log.v(TAG, "onRestore()");
242 int storedSystemVersion = -1;
Christopher Tate6785dd82009-06-18 15:58:25 -0700243
244 while (data.readNextHeader()) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700245 String key = data.getKey();
Christopher Tate6785dd82009-06-18 15:58:25 -0700246 int dataSize = data.getDataSize();
Christopher Tate6785dd82009-06-18 15:58:25 -0700247
Christopher Tate3a31a932009-06-22 15:10:30 -0700248 if (DEBUG) Log.v(TAG, " got key=" + key + " dataSize=" + dataSize);
Christopher Tate6aa41f42009-06-19 14:14:22 -0700249
Christopher Tate3a31a932009-06-22 15:10:30 -0700250 // generic setup to parse any entity data
251 byte[] dataBuf = new byte[dataSize];
252 data.readEntityData(dataBuf, 0, dataSize);
253 ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);
254 DataInputStream in = new DataInputStream(baStream);
255
256 if (key.equals(GLOBAL_METADATA_KEY)) {
Christopher Tate72d19aa2009-06-30 12:47:33 -0700257 int storedSdkVersion = in.readInt();
Christopher Tate3a31a932009-06-22 15:10:30 -0700258 if (DEBUG) Log.v(TAG, " storedSystemVersion = " + storedSystemVersion);
259 if (storedSystemVersion > Build.VERSION.SDK_INT) {
260 // returning before setting the sig map means we rejected the restore set
261 Log.w(TAG, "Restore set was from a later version of Android; not restoring");
262 return;
263 }
Christopher Tate72d19aa2009-06-30 12:47:33 -0700264 mStoredSdkVersion = storedSdkVersion;
265 mStoredIncrementalVersion = in.readUTF();
Christopher Tate3d7cd132009-07-07 14:23:07 -0700266 mHasMetadata = true;
Christopher Tate3a31a932009-06-22 15:10:30 -0700267 if (DEBUG) {
268 Log.i(TAG, "Restore set version " + storedSystemVersion
Christopher Tate72d19aa2009-06-30 12:47:33 -0700269 + " is compatible with OS version " + Build.VERSION.SDK_INT
270 + " (" + mStoredIncrementalVersion + " vs "
271 + Build.VERSION.INCREMENTAL + ")");
Christopher Tate3a31a932009-06-22 15:10:30 -0700272 }
273 } else {
274 // it's a file metadata record
275 int versionCode = in.readInt();
276 Signature[] sigs = unflattenSignatureArray(in);
Christopher Tate3a31a932009-06-22 15:10:30 -0700277 if (DEBUG) {
278 Log.i(TAG, " restored metadata for " + key
279 + " dataSize=" + dataSize
280 + " versionCode=" + versionCode + " sigs=" + sigs);
281 }
282
283 ApplicationInfo app = new ApplicationInfo();
284 app.packageName = key;
285 restoredApps.add(app);
286 sigMap.put(key, new Metadata(versionCode, sigs));
Christopher Tate6785dd82009-06-18 15:58:25 -0700287 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700288 }
289
Christopher Tate3a31a932009-06-22 15:10:30 -0700290 // On successful completion, cache the signature map for the Backup Manager to use
Christopher Tate6785dd82009-06-18 15:58:25 -0700291 mRestoredSignatures = sigMap;
292 }
293
294
295 // Util: convert an array of Signatures into a flattened byte buffer. The
296 // flattened format contains enough info to reconstruct the signature array.
297 private byte[] flattenSignatureArray(Signature[] allSigs) {
298 ByteArrayOutputStream outBuf = new ByteArrayOutputStream();
299 DataOutputStream out = new DataOutputStream(outBuf);
300
301 // build the set of subsidiary buffers
302 try {
303 // first the # of signatures in the array
304 out.writeInt(allSigs.length);
305
306 // then the signatures themselves, length + flattened buffer
307 for (Signature sig : allSigs) {
308 byte[] flat = sig.toByteArray();
309 out.writeInt(flat.length);
310 out.write(flat);
311 }
312 } catch (IOException e) {
313 // very strange; we're writing to memory here. abort.
314 return null;
315 }
316
317 return outBuf.toByteArray();
318 }
319
Christopher Tate6aa41f42009-06-19 14:14:22 -0700320 private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) {
Christopher Tate6785dd82009-06-18 15:58:25 -0700321 Signature[] sigs = null;
322
323 try {
324 int num = in.readInt();
Christopher Tateb808a9392009-09-29 16:09:52 -0700325 if (DEBUG) Log.v(TAG, " ... unflatten read " + num);
Christopher Tate5a8a1152009-09-10 16:08:47 -0700326
327 // Sensical?
328 if (num > 20) {
329 Log.e(TAG, "Suspiciously large sig count in restore data; aborting");
330 throw new IllegalStateException("Bad restore state");
331 }
332
Christopher Tate6785dd82009-06-18 15:58:25 -0700333 sigs = new Signature[num];
334 for (int i = 0; i < num; i++) {
335 int len = in.readInt();
336 byte[] flatSig = new byte[len];
337 in.read(flatSig);
338 sigs[i] = new Signature(flatSig);
339 }
340 } catch (EOFException e) {
341 // clean termination
342 if (sigs == null) {
343 Log.w(TAG, "Empty signature block found");
344 }
345 } catch (IOException e) {
Christopher Tateb808a9392009-09-29 16:09:52 -0700346 Log.e(TAG, "Unable to unflatten sigs");
Christopher Tate6785dd82009-06-18 15:58:25 -0700347 return null;
348 }
349
350 return sigs;
351 }
352
353 // Util: parse out an existing state file into a usable structure
Christopher Tate72d19aa2009-06-30 12:47:33 -0700354 private void parseStateFile(ParcelFileDescriptor stateFile) {
355 mExisting.clear();
356 mStateVersions.clear();
357 mStoredSdkVersion = 0;
358 mStoredIncrementalVersion = null;
359
Christopher Tate6785dd82009-06-18 15:58:25 -0700360 // The state file is just the list of app names we have stored signatures for
Christopher Tate72d19aa2009-06-30 12:47:33 -0700361 // with the exception of the metadata block, to which is also appended the
362 // version numbers corresponding with the last time we wrote this PM block.
363 // If they mismatch the current system, we'll re-store the metadata key.
Christopher Tate6785dd82009-06-18 15:58:25 -0700364 FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor());
365 DataInputStream in = new DataInputStream(instream);
366
367 int bufSize = 256;
368 byte[] buf = new byte[bufSize];
369 try {
Christopher Tate72d19aa2009-06-30 12:47:33 -0700370 String pkg = in.readUTF();
371 if (pkg.equals(GLOBAL_METADATA_KEY)) {
372 mStoredSdkVersion = in.readInt();
373 mStoredIncrementalVersion = in.readUTF();
374 mExisting.add(GLOBAL_METADATA_KEY);
375 } else {
376 Log.e(TAG, "No global metadata in state file!");
377 return;
Christopher Tate6785dd82009-06-18 15:58:25 -0700378 }
Christopher Tate72d19aa2009-06-30 12:47:33 -0700379
380 // The global metadata was first; now read all the apps
381 while (true) {
382 pkg = in.readUTF();
383 int versionCode = in.readInt();
384 mExisting.add(pkg);
385 mStateVersions.put(pkg, new Metadata(versionCode, null));
386 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700387 } catch (EOFException eof) {
388 // safe; we're done
389 } catch (IOException e) {
390 // whoops, bad state file. abort.
Christopher Tate72d19aa2009-06-30 12:47:33 -0700391 Log.e(TAG, "Unable to read Package Manager state file: " + e);
Christopher Tate6785dd82009-06-18 15:58:25 -0700392 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700393 }
394
Christopher Tate3a31a932009-06-22 15:10:30 -0700395 // Util: write out our new backup state file
Dan Egnorefe52642009-06-24 00:16:33 -0700396 private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
Christopher Tate6785dd82009-06-18 15:58:25 -0700397 FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
398 DataOutputStream out = new DataOutputStream(outstream);
399
Christopher Tate3a31a932009-06-22 15:10:30 -0700400 try {
401 // by the time we get here we know we've stored the global metadata record
Christopher Tate72d19aa2009-06-30 12:47:33 -0700402 out.writeUTF(GLOBAL_METADATA_KEY);
403 out.writeInt(Build.VERSION.SDK_INT);
404 out.writeUTF(Build.VERSION.INCREMENTAL);
Christopher Tate3a31a932009-06-22 15:10:30 -0700405
406 // now write all the app names too
Dan Egnorefe52642009-06-24 00:16:33 -0700407 for (PackageInfo pkg : pkgs) {
Christopher Tate72d19aa2009-06-30 12:47:33 -0700408 out.writeUTF(pkg.packageName);
409 out.writeInt(pkg.versionCode);
Christopher Tate6785dd82009-06-18 15:58:25 -0700410 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700411 } catch (IOException e) {
412 Log.e(TAG, "Unable to write package manager state file!");
413 return;
Christopher Tate6785dd82009-06-18 15:58:25 -0700414 }
415 }
416}