2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server;
19 import android.app.backup.BackupAgent;
20 import android.app.backup.BackupDataInput;
21 import android.app.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;
27 import android.os.Build;
28 import android.os.ParcelFileDescriptor;
29 import android.util.Slog;
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;
46 * We back up the signatures of each package so that during a system restore,
47 * we can verify that the app whose data we think we have matches the app
48 * actually resident on the device.
50 * Since the Package Manager isn't a proper "application" we just provide a
51 * direct IBackupAgent implementation and hand-construct it at need.
53 public class PackageManagerBackupAgent extends BackupAgent {
54 private static final String TAG = "PMBA";
55 private static final boolean DEBUG = false;
57 // key under which we store global metadata (individual app metadata
58 // is stored using the package name as a key)
59 private static final String GLOBAL_METADATA_KEY = "@meta@";
61 private List<PackageInfo> mAllPackages;
62 private PackageManager mPackageManager;
63 // version & signature info of each app in a restore set
64 private HashMap<String, Metadata> mRestoredSignatures;
65 // The version info of each backed-up app as read from the state file
66 private HashMap<String, Metadata> mStateVersions = new HashMap<String, Metadata>();
68 private final HashSet<String> mExisting = new HashSet<String>();
69 private int mStoredSdkVersion;
70 private String mStoredIncrementalVersion;
71 private boolean mHasMetadata;
73 public class Metadata {
74 public int versionCode;
75 public Signature[] signatures;
77 Metadata(int version, Signature[] sigs) {
78 versionCode = version;
83 // We're constructed with the set of applications that are participating
84 // in backup. This set changes as apps are installed & removed.
85 PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
86 mPackageManager = packageMgr;
87 mAllPackages = packages;
88 mRestoredSignatures = null;
92 public boolean hasMetadata() {
96 public Metadata getRestoredMetadata(String packageName) {
97 if (mRestoredSignatures == null) {
98 Slog.w(TAG, "getRestoredMetadata() before metadata read!");
102 return mRestoredSignatures.get(packageName);
105 public Set<String> getRestoredPackages() {
106 if (mRestoredSignatures == null) {
107 Slog.w(TAG, "getRestoredPackages() before metadata read!");
111 // This is technically the set of packages on the originating handset
112 // that had backup agents at all, not limited to the set of packages
113 // that had actually contributed a restore dataset, but it's a
114 // close enough approximation for our purposes and does not require any
115 // additional involvement by the transport to obtain.
116 return mRestoredSignatures.keySet();
119 // The backed up data is the signature block for each app, keyed by
121 public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
122 ParcelFileDescriptor newState) {
123 if (DEBUG) Slog.v(TAG, "onBackup()");
125 ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream(); // we'll reuse these
126 DataOutputStream outputBufferStream = new DataOutputStream(outputBuffer);
127 parseStateFile(oldState);
129 // If the stored version string differs, we need to re-backup all
130 // of the metadata. We force this by removing everything from the
131 // "already backed up" map built by parseStateFile().
132 if (mStoredIncrementalVersion == null
133 || !mStoredIncrementalVersion.equals(Build.VERSION.INCREMENTAL)) {
134 Slog.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs "
135 + Build.VERSION.INCREMENTAL + " - rewriting");
143 * int SDKversion -- the SDK version of the OS itself on the device
144 * that produced this backup set. Used to reject
145 * backups from later OSes onto earlier ones.
146 * String incremental -- the incremental release name of the OS stored in
149 if (!mExisting.contains(GLOBAL_METADATA_KEY)) {
150 if (DEBUG) Slog.v(TAG, "Storing global metadata key");
151 outputBufferStream.writeInt(Build.VERSION.SDK_INT);
152 outputBufferStream.writeUTF(Build.VERSION.INCREMENTAL);
153 writeEntity(data, GLOBAL_METADATA_KEY, outputBuffer.toByteArray());
155 if (DEBUG) Slog.v(TAG, "Global metadata key already stored");
156 // don't consider it to have been skipped/deleted
157 mExisting.remove(GLOBAL_METADATA_KEY);
160 // For each app we have on device, see if we've backed it up yet. If not,
161 // write its signature block to the output, keyed on the package name.
162 for (PackageInfo pkg : mAllPackages) {
163 String packName = pkg.packageName;
164 if (packName.equals(GLOBAL_METADATA_KEY)) {
165 // We've already handled the metadata key; skip it here
168 PackageInfo info = null;
170 info = mPackageManager.getPackageInfo(packName,
171 PackageManager.GET_SIGNATURES);
172 } catch (NameNotFoundException e) {
173 // Weird; we just found it, and now are told it doesn't exist.
174 // Treat it as having been removed from the device.
175 mExisting.add(packName);
179 if (mExisting.contains(packName)) {
180 // We have backed up this app before. Check whether the version
181 // of the backup matches the version of the current app; if they
182 // don't match, the app has been updated and we need to store its
183 // metadata again. In either case, take it out of mExisting so that
184 // we don't consider it deleted later.
185 mExisting.remove(packName);
186 if (info.versionCode == mStateVersions.get(packName).versionCode) {
191 if (info.signatures == null || info.signatures.length == 0)
193 Slog.w(TAG, "Not backing up package " + packName
194 + " since it appears to have no signatures.");
198 // We need to store this app's metadata
200 * Metadata for each package:
202 * int version -- [4] the package's versionCode
203 * byte[] signatures -- [len] flattened Signature[] of the package
206 // marshal the version code in a canonical form
207 outputBuffer.reset();
208 outputBufferStream.writeInt(info.versionCode);
209 writeSignatureArray(outputBufferStream, info.signatures);
212 Slog.v(TAG, "+ writing metadata for " + packName
213 + " version=" + info.versionCode
214 + " entityLen=" + outputBuffer.size());
217 // Now we can write the backup entity for this package
218 writeEntity(data, packName, outputBuffer.toByteArray());
222 // At this point, the only entries in 'existing' are apps that were
223 // mentioned in the saved state file, but appear to no longer be present
224 // on the device. Write a deletion entity for them.
225 for (String app : mExisting) {
226 if (DEBUG) Slog.v(TAG, "- removing metadata for deleted pkg " + app);
228 data.writeEntityHeader(app, -1);
229 } catch (IOException e) {
230 Slog.e(TAG, "Unable to write package deletions!");
234 } catch (IOException e) {
235 // Real error writing data
236 Slog.e(TAG, "Unable to write package backup data file!");
240 // Finally, write the new state blob -- just the list of all apps we handled
241 writeStateFile(mAllPackages, newState);
244 private static void writeEntity(BackupDataOutput data, String key, byte[] bytes)
246 data.writeEntityHeader(key, bytes.length);
247 data.writeEntityData(bytes, bytes.length);
250 // "Restore" here is a misnomer. What we're really doing is reading back the
251 // set of app signatures associated with each backed-up app in this restore
252 // image. We'll use those later to determine what we can legitimately restore.
253 public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
255 List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
256 HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>();
257 if (DEBUG) Slog.v(TAG, "onRestore()");
258 int storedSystemVersion = -1;
260 while (data.readNextHeader()) {
261 String key = data.getKey();
262 int dataSize = data.getDataSize();
264 if (DEBUG) Slog.v(TAG, " got key=" + key + " dataSize=" + dataSize);
266 // generic setup to parse any entity data
267 byte[] inputBytes = new byte[dataSize];
268 data.readEntityData(inputBytes, 0, dataSize);
269 ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes);
270 DataInputStream inputBufferStream = new DataInputStream(inputBuffer);
272 if (key.equals(GLOBAL_METADATA_KEY)) {
273 int storedSdkVersion = inputBufferStream.readInt();
274 if (DEBUG) Slog.v(TAG, " storedSystemVersion = " + storedSystemVersion);
275 if (storedSystemVersion > Build.VERSION.SDK_INT) {
276 // returning before setting the sig map means we rejected the restore set
277 Slog.w(TAG, "Restore set was from a later version of Android; not restoring");
280 mStoredSdkVersion = storedSdkVersion;
281 mStoredIncrementalVersion = inputBufferStream.readUTF();
284 Slog.i(TAG, "Restore set version " + storedSystemVersion
285 + " is compatible with OS version " + Build.VERSION.SDK_INT
286 + " (" + mStoredIncrementalVersion + " vs "
287 + Build.VERSION.INCREMENTAL + ")");
290 // it's a file metadata record
291 int versionCode = inputBufferStream.readInt();
292 Signature[] sigs = readSignatureArray(inputBufferStream);
294 Slog.i(TAG, " read metadata for " + key
295 + " dataSize=" + dataSize
296 + " versionCode=" + versionCode + " sigs=" + sigs);
299 if (sigs == null || sigs.length == 0) {
300 Slog.w(TAG, "Not restoring package " + key
301 + " since it appears to have no signatures.");
305 ApplicationInfo app = new ApplicationInfo();
306 app.packageName = key;
307 restoredApps.add(app);
308 sigMap.put(key, new Metadata(versionCode, sigs));
312 // On successful completion, cache the signature map for the Backup Manager to use
313 mRestoredSignatures = sigMap;
316 private static void writeSignatureArray(DataOutputStream out, Signature[] sigs)
318 // write the number of signatures in the array
319 out.writeInt(sigs.length);
321 // write the signatures themselves, length + flattened buffer
322 for (Signature sig : sigs) {
323 byte[] flat = sig.toByteArray();
324 out.writeInt(flat.length);
329 private static Signature[] readSignatureArray(DataInputStream in) {
334 } catch (EOFException e) {
336 Slog.w(TAG, "Read empty signature block");
340 if (DEBUG) Slog.v(TAG, " ... unflatten read " + num);
344 Slog.e(TAG, "Suspiciously large sig count in restore data; aborting");
345 throw new IllegalStateException("Bad restore state");
348 Signature[] sigs = new Signature[num];
349 for (int i = 0; i < num; i++) {
350 int len = in.readInt();
351 byte[] flatSig = new byte[len];
353 sigs[i] = new Signature(flatSig);
356 } catch (IOException e) {
357 Slog.e(TAG, "Unable to read signatures");
362 // Util: parse out an existing state file into a usable structure
363 private void parseStateFile(ParcelFileDescriptor stateFile) {
365 mStateVersions.clear();
366 mStoredSdkVersion = 0;
367 mStoredIncrementalVersion = null;
369 // The state file is just the list of app names we have stored signatures for
370 // with the exception of the metadata block, to which is also appended the
371 // version numbers corresponding with the last time we wrote this PM block.
372 // If they mismatch the current system, we'll re-store the metadata key.
373 FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor());
374 DataInputStream in = new DataInputStream(instream);
377 byte[] buf = new byte[bufSize];
379 String pkg = in.readUTF();
380 if (pkg.equals(GLOBAL_METADATA_KEY)) {
381 mStoredSdkVersion = in.readInt();
382 mStoredIncrementalVersion = in.readUTF();
383 mExisting.add(GLOBAL_METADATA_KEY);
385 Slog.e(TAG, "No global metadata in state file!");
389 // The global metadata was first; now read all the apps
392 int versionCode = in.readInt();
394 mStateVersions.put(pkg, new Metadata(versionCode, null));
396 } catch (EOFException eof) {
398 } catch (IOException e) {
399 // whoops, bad state file. abort.
400 Slog.e(TAG, "Unable to read Package Manager state file: " + e);
404 // Util: write out our new backup state file
405 private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
406 FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
407 DataOutputStream out = new DataOutputStream(outstream);
410 // by the time we get here we know we've stored the global metadata record
411 out.writeUTF(GLOBAL_METADATA_KEY);
412 out.writeInt(Build.VERSION.SDK_INT);
413 out.writeUTF(Build.VERSION.INCREMENTAL);
415 // now write all the app names too
416 for (PackageInfo pkg : pkgs) {
417 out.writeUTF(pkg.packageName);
418 out.writeInt(pkg.versionCode);
420 } catch (IOException e) {
421 Slog.e(TAG, "Unable to write package manager state file!");