blob: 786f42305c96a00eb1616aa69b7eebef33dc1876 [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 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
198 // !!! TODO: take out this debugging
199 if (DEBUG) {
200 Log.v(TAG, "+ metadata for " + packName
201 + " version=" + info.versionCode
202 + " versionLen=" + versionBuf.length
203 + " sigsLen=" + sigs.length);
204 }
205 // Now we can write the backup entity for this package
206 data.writeEntityHeader(packName, versionBuf.length + sigs.length);
207 data.writeEntityData(versionBuf, versionBuf.length);
208 data.writeEntityData(sigs, sigs.length);
Christopher Tate3a31a932009-06-22 15:10:30 -0700209 }
210 }
211 }
212
213 // At this point, the only entries in 'existing' are apps that were
214 // mentioned in the saved state file, but appear to no longer be present
215 // on the device. Write a deletion entity for them.
Christopher Tate72d19aa2009-06-30 12:47:33 -0700216 for (String app : mExisting) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700217 // !!! TODO: take out this msg
218 if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app);
219 try {
220 data.writeEntityHeader(app, -1);
Christopher Tate6785dd82009-06-18 15:58:25 -0700221 } catch (IOException e) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700222 Log.e(TAG, "Unable to write package deletions!");
Christopher Tate6785dd82009-06-18 15:58:25 -0700223 return;
224 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700225 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700226 } catch (IOException e) {
227 // Real error writing data
228 Log.e(TAG, "Unable to write package backup data file!");
229 return;
Christopher Tate6785dd82009-06-18 15:58:25 -0700230 }
231
232 // Finally, write the new state blob -- just the list of all apps we handled
Dan Egnorefe52642009-06-24 00:16:33 -0700233 writeStateFile(mAllPackages, newState);
Christopher Tate6785dd82009-06-18 15:58:25 -0700234 }
235
236 // "Restore" here is a misnomer. What we're really doing is reading back the
237 // set of app signatures associated with each backed-up app in this restore
238 // image. We'll use those later to determine what we can legitimately restore.
Christopher Tate5cbbf562009-06-22 16:44:51 -0700239 public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
Christopher Tate6785dd82009-06-18 15:58:25 -0700240 throws IOException {
241 List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
Christopher Tate6aa41f42009-06-19 14:14:22 -0700242 HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>();
Christopher Tate3a31a932009-06-22 15:10:30 -0700243 if (DEBUG) Log.v(TAG, "onRestore()");
244 int storedSystemVersion = -1;
Christopher Tate6785dd82009-06-18 15:58:25 -0700245
246 while (data.readNextHeader()) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700247 String key = data.getKey();
Christopher Tate6785dd82009-06-18 15:58:25 -0700248 int dataSize = data.getDataSize();
Christopher Tate6785dd82009-06-18 15:58:25 -0700249
Christopher Tate3a31a932009-06-22 15:10:30 -0700250 if (DEBUG) Log.v(TAG, " got key=" + key + " dataSize=" + dataSize);
Christopher Tate6aa41f42009-06-19 14:14:22 -0700251
Christopher Tate3a31a932009-06-22 15:10:30 -0700252 // generic setup to parse any entity data
253 byte[] dataBuf = new byte[dataSize];
254 data.readEntityData(dataBuf, 0, dataSize);
255 ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);
256 DataInputStream in = new DataInputStream(baStream);
257
258 if (key.equals(GLOBAL_METADATA_KEY)) {
Christopher Tate72d19aa2009-06-30 12:47:33 -0700259 int storedSdkVersion = in.readInt();
Christopher Tate3a31a932009-06-22 15:10:30 -0700260 if (DEBUG) Log.v(TAG, " storedSystemVersion = " + storedSystemVersion);
261 if (storedSystemVersion > Build.VERSION.SDK_INT) {
262 // returning before setting the sig map means we rejected the restore set
263 Log.w(TAG, "Restore set was from a later version of Android; not restoring");
264 return;
265 }
Christopher Tate72d19aa2009-06-30 12:47:33 -0700266 mStoredSdkVersion = storedSdkVersion;
267 mStoredIncrementalVersion = in.readUTF();
Christopher Tate3d7cd132009-07-07 14:23:07 -0700268 mHasMetadata = true;
Christopher Tate3a31a932009-06-22 15:10:30 -0700269 // !!! TODO: remove this debugging output
270 if (DEBUG) {
271 Log.i(TAG, "Restore set version " + storedSystemVersion
Christopher Tate72d19aa2009-06-30 12:47:33 -0700272 + " is compatible with OS version " + Build.VERSION.SDK_INT
273 + " (" + mStoredIncrementalVersion + " vs "
274 + Build.VERSION.INCREMENTAL + ")");
Christopher Tate3a31a932009-06-22 15:10:30 -0700275 }
276 } else {
277 // it's a file metadata record
278 int versionCode = in.readInt();
279 Signature[] sigs = unflattenSignatureArray(in);
280// !!! TODO: take out this debugging
281 if (DEBUG) {
282 Log.i(TAG, " restored metadata for " + key
283 + " dataSize=" + dataSize
284 + " versionCode=" + versionCode + " sigs=" + sigs);
285 }
286
287 ApplicationInfo app = new ApplicationInfo();
288 app.packageName = key;
289 restoredApps.add(app);
290 sigMap.put(key, new Metadata(versionCode, sigs));
Christopher Tate6785dd82009-06-18 15:58:25 -0700291 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700292 }
293
Christopher Tate3a31a932009-06-22 15:10:30 -0700294 // On successful completion, cache the signature map for the Backup Manager to use
Christopher Tate6785dd82009-06-18 15:58:25 -0700295 mRestoredSignatures = sigMap;
296 }
297
298
299 // Util: convert an array of Signatures into a flattened byte buffer. The
300 // flattened format contains enough info to reconstruct the signature array.
301 private byte[] flattenSignatureArray(Signature[] allSigs) {
302 ByteArrayOutputStream outBuf = new ByteArrayOutputStream();
303 DataOutputStream out = new DataOutputStream(outBuf);
304
305 // build the set of subsidiary buffers
306 try {
307 // first the # of signatures in the array
308 out.writeInt(allSigs.length);
309
310 // then the signatures themselves, length + flattened buffer
311 for (Signature sig : allSigs) {
312 byte[] flat = sig.toByteArray();
313 out.writeInt(flat.length);
314 out.write(flat);
315 }
316 } catch (IOException e) {
317 // very strange; we're writing to memory here. abort.
318 return null;
319 }
320
321 return outBuf.toByteArray();
322 }
323
Christopher Tate6aa41f42009-06-19 14:14:22 -0700324 private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) {
Christopher Tate6785dd82009-06-18 15:58:25 -0700325 Signature[] sigs = null;
326
327 try {
328 int num = in.readInt();
Christopher Tate3a31a932009-06-22 15:10:30 -0700329 Log.v(TAG, " ... unflatten read " + num);
Christopher Tate6785dd82009-06-18 15:58:25 -0700330 sigs = new Signature[num];
331 for (int i = 0; i < num; i++) {
332 int len = in.readInt();
333 byte[] flatSig = new byte[len];
334 in.read(flatSig);
335 sigs[i] = new Signature(flatSig);
336 }
337 } catch (EOFException e) {
338 // clean termination
339 if (sigs == null) {
340 Log.w(TAG, "Empty signature block found");
341 }
342 } catch (IOException e) {
343 Log.d(TAG, "Unable to unflatten sigs");
344 return null;
345 }
346
347 return sigs;
348 }
349
350 // Util: parse out an existing state file into a usable structure
Christopher Tate72d19aa2009-06-30 12:47:33 -0700351 private void parseStateFile(ParcelFileDescriptor stateFile) {
352 mExisting.clear();
353 mStateVersions.clear();
354 mStoredSdkVersion = 0;
355 mStoredIncrementalVersion = null;
356
Christopher Tate6785dd82009-06-18 15:58:25 -0700357 // The state file is just the list of app names we have stored signatures for
Christopher Tate72d19aa2009-06-30 12:47:33 -0700358 // with the exception of the metadata block, to which is also appended the
359 // version numbers corresponding with the last time we wrote this PM block.
360 // If they mismatch the current system, we'll re-store the metadata key.
Christopher Tate6785dd82009-06-18 15:58:25 -0700361 FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor());
362 DataInputStream in = new DataInputStream(instream);
363
364 int bufSize = 256;
365 byte[] buf = new byte[bufSize];
366 try {
Christopher Tate72d19aa2009-06-30 12:47:33 -0700367 String pkg = in.readUTF();
368 if (pkg.equals(GLOBAL_METADATA_KEY)) {
369 mStoredSdkVersion = in.readInt();
370 mStoredIncrementalVersion = in.readUTF();
371 mExisting.add(GLOBAL_METADATA_KEY);
372 } else {
373 Log.e(TAG, "No global metadata in state file!");
374 return;
Christopher Tate6785dd82009-06-18 15:58:25 -0700375 }
Christopher Tate72d19aa2009-06-30 12:47:33 -0700376
377 // The global metadata was first; now read all the apps
378 while (true) {
379 pkg = in.readUTF();
380 int versionCode = in.readInt();
381 mExisting.add(pkg);
382 mStateVersions.put(pkg, new Metadata(versionCode, null));
383 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700384 } catch (EOFException eof) {
385 // safe; we're done
386 } catch (IOException e) {
387 // whoops, bad state file. abort.
Christopher Tate72d19aa2009-06-30 12:47:33 -0700388 Log.e(TAG, "Unable to read Package Manager state file: " + e);
Christopher Tate6785dd82009-06-18 15:58:25 -0700389 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700390 }
391
Christopher Tate3a31a932009-06-22 15:10:30 -0700392 // Util: write out our new backup state file
Dan Egnorefe52642009-06-24 00:16:33 -0700393 private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
Christopher Tate6785dd82009-06-18 15:58:25 -0700394 FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
395 DataOutputStream out = new DataOutputStream(outstream);
396
Christopher Tate3a31a932009-06-22 15:10:30 -0700397 try {
398 // by the time we get here we know we've stored the global metadata record
Christopher Tate72d19aa2009-06-30 12:47:33 -0700399 out.writeUTF(GLOBAL_METADATA_KEY);
400 out.writeInt(Build.VERSION.SDK_INT);
401 out.writeUTF(Build.VERSION.INCREMENTAL);
Christopher Tate3a31a932009-06-22 15:10:30 -0700402
403 // now write all the app names too
Dan Egnorefe52642009-06-24 00:16:33 -0700404 for (PackageInfo pkg : pkgs) {
Christopher Tate72d19aa2009-06-30 12:47:33 -0700405 out.writeUTF(pkg.packageName);
406 out.writeInt(pkg.versionCode);
Christopher Tate6785dd82009-06-18 15:58:25 -0700407 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700408 } catch (IOException e) {
409 Log.e(TAG, "Unable to write package manager state file!");
410 return;
Christopher Tate6785dd82009-06-18 15:58:25 -0700411 }
412 }
413}