blob: a1b4c26833dea6201a0fda10b91e6233cdb30d42 [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";
54 private static final boolean DEBUG = true;
55
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 Tate6aa41f42009-06-19 14:14:22 -070070
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 Tate6785dd82009-06-18 15:58:25 -070080
81 // We're constructed with the set of applications that are participating
82 // in backup. This set changes as apps are installed & removed.
Dan Egnorefe52642009-06-24 00:16:33 -070083 PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
Christopher Tate6785dd82009-06-18 15:58:25 -070084 mPackageManager = packageMgr;
Dan Egnorefe52642009-06-24 00:16:33 -070085 mAllPackages = packages;
Christopher Tate6785dd82009-06-18 15:58:25 -070086 mRestoredSignatures = null;
87 }
88
Christopher Tate6aa41f42009-06-19 14:14:22 -070089 public Metadata getRestoredMetadata(String packageName) {
Christopher Tate6785dd82009-06-18 15:58:25 -070090 if (mRestoredSignatures == null) {
Christopher Tate3a31a932009-06-22 15:10:30 -070091 Log.w(TAG, "getRestoredMetadata() before metadata read!");
Christopher Tate6785dd82009-06-18 15:58:25 -070092 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 Tate6aa41f42009-06-19 14:14:22 -0700102 if (DEBUG) Log.v(TAG, "onBackup()");
Christopher Tate3a31a932009-06-22 15:10:30 -0700103
Christopher Tate6aa41f42009-06-19 14:14:22 -0700104 ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); // we'll reuse these
105 DataOutputStream outWriter = new DataOutputStream(bufStream);
Christopher Tate72d19aa2009-06-30 12:47:33 -0700106 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 Tate6aa41f42009-06-19 14:14:22 -0700117
Christopher Tate3a31a932009-06-22 15:10:30 -0700118 try {
119 /*
120 * Global metadata:
121 *
Christopher Tate72d19aa2009-06-30 12:47:33 -0700122 * 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 Tate3a31a932009-06-22 15:10:30 -0700127 */
Christopher Tate72d19aa2009-06-30 12:47:33 -0700128 if (!mExisting.contains(GLOBAL_METADATA_KEY)) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700129 if (DEBUG) Log.v(TAG, "Storing global metadata key");
130 outWriter.writeInt(Build.VERSION.SDK_INT);
Christopher Tate72d19aa2009-06-30 12:47:33 -0700131 outWriter.writeUTF(Build.VERSION.INCREMENTAL);
Christopher Tate3a31a932009-06-22 15:10:30 -0700132 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 Tate72d19aa2009-06-30 12:47:33 -0700137 // don't consider it to have been skipped/deleted
138 mExisting.remove(GLOBAL_METADATA_KEY);
Christopher Tate3a31a932009-06-22 15:10:30 -0700139 }
Christopher Tate6aa41f42009-06-19 14:14:22 -0700140
Christopher Tate3a31a932009-06-22 15:10:30 -0700141 // 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 Egnorefe52642009-06-24 00:16:33 -0700143 for (PackageInfo pkg : mAllPackages) {
144 String packName = pkg.packageName;
Christopher Tate6f317422009-06-29 18:52:55 -0700145 if (packName.equals(GLOBAL_METADATA_KEY)) {
146 // We've already handled the metadata key; skip it here
147 continue;
Christopher Tate72d19aa2009-06-30 12:47:33 -0700148 } else {
149 PackageInfo info = null;
Christopher Tate3a31a932009-06-22 15:10:30 -0700150 try {
Christopher Tate72d19aa2009-06-30 12:47:33 -0700151 info = mPackageManager.getPackageInfo(packName,
Christopher Tate3a31a932009-06-22 15:10:30 -0700152 PackageManager.GET_SIGNATURES);
Christopher Tate72d19aa2009-06-30 12:47:33 -0700153 } 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 Tate3a31a932009-06-22 15:10:30 -0700178 /*
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 Tate6aa41f42009-06-19 14:14:22 -0700184
Christopher Tate72d19aa2009-06-30 12:47:33 -0700185 // marshal the version code in a canonical form
Christopher Tate3a31a932009-06-22 15:10:30 -0700186 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 Tate3a31a932009-06-22 15:10:30 -0700203 }
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 Tate72d19aa2009-06-30 12:47:33 -0700210 for (String app : mExisting) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700211 // !!! 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 Tate6785dd82009-06-18 15:58:25 -0700215 } catch (IOException e) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700216 Log.e(TAG, "Unable to write package deletions!");
Christopher Tate6785dd82009-06-18 15:58:25 -0700217 return;
218 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700219 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700220 } catch (IOException e) {
221 // Real error writing data
222 Log.e(TAG, "Unable to write package backup data file!");
223 return;
Christopher Tate6785dd82009-06-18 15:58:25 -0700224 }
225
226 // Finally, write the new state blob -- just the list of all apps we handled
Dan Egnorefe52642009-06-24 00:16:33 -0700227 writeStateFile(mAllPackages, newState);
Christopher Tate6785dd82009-06-18 15:58:25 -0700228 }
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 Tate5cbbf562009-06-22 16:44:51 -0700233 public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
Christopher Tate6785dd82009-06-18 15:58:25 -0700234 throws IOException {
235 List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
Christopher Tate6aa41f42009-06-19 14:14:22 -0700236 HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>();
Christopher Tate3a31a932009-06-22 15:10:30 -0700237 if (DEBUG) Log.v(TAG, "onRestore()");
238 int storedSystemVersion = -1;
Christopher Tate6785dd82009-06-18 15:58:25 -0700239
240 while (data.readNextHeader()) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700241 String key = data.getKey();
Christopher Tate6785dd82009-06-18 15:58:25 -0700242 int dataSize = data.getDataSize();
Christopher Tate6785dd82009-06-18 15:58:25 -0700243
Christopher Tate3a31a932009-06-22 15:10:30 -0700244 if (DEBUG) Log.v(TAG, " got key=" + key + " dataSize=" + dataSize);
Christopher Tate6aa41f42009-06-19 14:14:22 -0700245
Christopher Tate3a31a932009-06-22 15:10:30 -0700246 // 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 Tate72d19aa2009-06-30 12:47:33 -0700253 int storedSdkVersion = in.readInt();
Christopher Tate3a31a932009-06-22 15:10:30 -0700254 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 Tate72d19aa2009-06-30 12:47:33 -0700260 mStoredSdkVersion = storedSdkVersion;
261 mStoredIncrementalVersion = in.readUTF();
Christopher Tate3a31a932009-06-22 15:10:30 -0700262 // !!! TODO: remove this debugging output
263 if (DEBUG) {
264 Log.i(TAG, "Restore set version " + storedSystemVersion
Christopher Tate72d19aa2009-06-30 12:47:33 -0700265 + " is compatible with OS version " + Build.VERSION.SDK_INT
266 + " (" + mStoredIncrementalVersion + " vs "
267 + Build.VERSION.INCREMENTAL + ")");
Christopher Tate3a31a932009-06-22 15:10:30 -0700268 }
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 Tate6785dd82009-06-18 15:58:25 -0700284 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700285 }
286
Christopher Tate3a31a932009-06-22 15:10:30 -0700287 // On successful completion, cache the signature map for the Backup Manager to use
Christopher Tate6785dd82009-06-18 15:58:25 -0700288 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 Tate6aa41f42009-06-19 14:14:22 -0700317 private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) {
Christopher Tate6785dd82009-06-18 15:58:25 -0700318 Signature[] sigs = null;
319
320 try {
321 int num = in.readInt();
Christopher Tate3a31a932009-06-22 15:10:30 -0700322 Log.v(TAG, " ... unflatten read " + num);
Christopher Tate6785dd82009-06-18 15:58:25 -0700323 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 Tate72d19aa2009-06-30 12:47:33 -0700344 private void parseStateFile(ParcelFileDescriptor stateFile) {
345 mExisting.clear();
346 mStateVersions.clear();
347 mStoredSdkVersion = 0;
348 mStoredIncrementalVersion = null;
349
Christopher Tate6785dd82009-06-18 15:58:25 -0700350 // The state file is just the list of app names we have stored signatures for
Christopher Tate72d19aa2009-06-30 12:47:33 -0700351 // 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 Tate6785dd82009-06-18 15:58:25 -0700354 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 Tate72d19aa2009-06-30 12:47:33 -0700360 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 Tate6785dd82009-06-18 15:58:25 -0700368 }
Christopher Tate72d19aa2009-06-30 12:47:33 -0700369
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 Tate6785dd82009-06-18 15:58:25 -0700377 } catch (EOFException eof) {
378 // safe; we're done
379 } catch (IOException e) {
380 // whoops, bad state file. abort.
Christopher Tate72d19aa2009-06-30 12:47:33 -0700381 Log.e(TAG, "Unable to read Package Manager state file: " + e);
Christopher Tate6785dd82009-06-18 15:58:25 -0700382 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700383 }
384
Christopher Tate3a31a932009-06-22 15:10:30 -0700385 // Util: write out our new backup state file
Dan Egnorefe52642009-06-24 00:16:33 -0700386 private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
Christopher Tate6785dd82009-06-18 15:58:25 -0700387 FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
388 DataOutputStream out = new DataOutputStream(outstream);
389
Christopher Tate3a31a932009-06-22 15:10:30 -0700390 try {
391 // by the time we get here we know we've stored the global metadata record
Christopher Tate72d19aa2009-06-30 12:47:33 -0700392 out.writeUTF(GLOBAL_METADATA_KEY);
393 out.writeInt(Build.VERSION.SDK_INT);
394 out.writeUTF(Build.VERSION.INCREMENTAL);
Christopher Tate3a31a932009-06-22 15:10:30 -0700395
396 // now write all the app names too
Dan Egnorefe52642009-06-24 00:16:33 -0700397 for (PackageInfo pkg : pkgs) {
Christopher Tate72d19aa2009-06-30 12:47:33 -0700398 out.writeUTF(pkg.packageName);
399 out.writeInt(pkg.versionCode);
Christopher Tate6785dd82009-06-18 15:58:25 -0700400 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700401 } catch (IOException e) {
402 Log.e(TAG, "Unable to write package manager state file!");
403 return;
Christopher Tate6785dd82009-06-18 15:58:25 -0700404 }
405 }
406}