blob: 9422878f723f7f83fdca698d2446505aca01dbdd [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
Christopher Tate6785dd82009-06-18 15:58:25 -070060 private List<ApplicationInfo> mAllApps;
61 private PackageManager mPackageManager;
Christopher Tate6aa41f42009-06-19 14:14:22 -070062 private HashMap<String, Metadata> mRestoredSignatures;
63
64 public class Metadata {
65 public int versionCode;
66 public Signature[] signatures;
67
68 Metadata(int version, Signature[] sigs) {
69 versionCode = version;
70 signatures = sigs;
71 }
72 }
Christopher Tate6785dd82009-06-18 15:58:25 -070073
74 // We're constructed with the set of applications that are participating
75 // in backup. This set changes as apps are installed & removed.
76 PackageManagerBackupAgent(PackageManager packageMgr, List<ApplicationInfo> apps) {
77 mPackageManager = packageMgr;
78 mAllApps = apps;
79 mRestoredSignatures = null;
80 }
81
Christopher Tate6aa41f42009-06-19 14:14:22 -070082 public Metadata getRestoredMetadata(String packageName) {
Christopher Tate6785dd82009-06-18 15:58:25 -070083 if (mRestoredSignatures == null) {
Christopher Tate3a31a932009-06-22 15:10:30 -070084 Log.w(TAG, "getRestoredMetadata() before metadata read!");
Christopher Tate6785dd82009-06-18 15:58:25 -070085 return null;
86 }
87
88 return mRestoredSignatures.get(packageName);
89 }
90
91 // The backed up data is the signature block for each app, keyed by
92 // the package name.
93 public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
94 ParcelFileDescriptor newState) {
Christopher Tate6aa41f42009-06-19 14:14:22 -070095 if (DEBUG) Log.v(TAG, "onBackup()");
Christopher Tate3a31a932009-06-22 15:10:30 -070096
Christopher Tate6aa41f42009-06-19 14:14:22 -070097 ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); // we'll reuse these
98 DataOutputStream outWriter = new DataOutputStream(bufStream);
Christopher Tate3a31a932009-06-22 15:10:30 -070099 HashSet<String> existing = parseStateFile(oldState);
Christopher Tate6aa41f42009-06-19 14:14:22 -0700100
Christopher Tate3a31a932009-06-22 15:10:30 -0700101 try {
102 /*
103 * Global metadata:
104 *
105 * int version -- the SDK version of the OS itself on the device
106 * that produced this backup set. Used to reject
107 * backups from later OSes onto earlier ones.
108 */
109 if (!existing.contains(GLOBAL_METADATA_KEY)) {
110 if (DEBUG) Log.v(TAG, "Storing global metadata key");
111 outWriter.writeInt(Build.VERSION.SDK_INT);
112 byte[] metadata = bufStream.toByteArray();
113 data.writeEntityHeader(GLOBAL_METADATA_KEY, metadata.length);
114 data.writeEntityData(metadata, metadata.length);
115 } else {
116 if (DEBUG) Log.v(TAG, "Global metadata key already stored");
117 }
Christopher Tate6aa41f42009-06-19 14:14:22 -0700118
Christopher Tate3a31a932009-06-22 15:10:30 -0700119 // For each app we have on device, see if we've backed it up yet. If not,
120 // write its signature block to the output, keyed on the package name.
121 for (ApplicationInfo app : mAllApps) {
122 String packName = app.packageName;
123 if (!existing.contains(packName)) {
124 // We haven't stored this app's signatures yet, so we do that now
125 try {
126 PackageInfo info = mPackageManager.getPackageInfo(packName,
127 PackageManager.GET_SIGNATURES);
128 /*
129 * Metadata for each package:
130 *
131 * int version -- [4] the package's versionCode
132 * byte[] signatures -- [len] flattened Signature[] of the package
133 */
Christopher Tate6aa41f42009-06-19 14:14:22 -0700134
Christopher Tate3a31a932009-06-22 15:10:30 -0700135 // marshall the version code in a canonical form
136 bufStream.reset();
137 outWriter.writeInt(info.versionCode);
138 byte[] versionBuf = bufStream.toByteArray();
139
140 byte[] sigs = flattenSignatureArray(info.signatures);
141
142 // !!! TODO: take out this debugging
143 if (DEBUG) {
144 Log.v(TAG, "+ metadata for " + packName
145 + " version=" + info.versionCode
146 + " versionLen=" + versionBuf.length
147 + " sigsLen=" + sigs.length);
148 }
149 // Now we can write the backup entity for this package
150 data.writeEntityHeader(packName, versionBuf.length + sigs.length);
151 data.writeEntityData(versionBuf, versionBuf.length);
152 data.writeEntityData(sigs, sigs.length);
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 existing.add(packName);
Christopher Tate6785dd82009-06-18 15:58:25 -0700157 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700158 } else {
159 // We've already backed up this app. Remove it from the set so
160 // we can tell at the end what has disappeared from the device.
161 // !!! TODO: take out the debugging message
162 if (DEBUG) Log.v(TAG, "= already backed up metadata for " + packName);
163 if (!existing.remove(packName)) {
164 Log.d(TAG, "*** failed to remove " + packName + " from package set!");
165 }
166 }
167 }
168
169 // At this point, the only entries in 'existing' are apps that were
170 // mentioned in the saved state file, but appear to no longer be present
171 // on the device. Write a deletion entity for them.
172 for (String app : existing) {
173 // !!! TODO: take out this msg
174 if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app);
175 try {
176 data.writeEntityHeader(app, -1);
Christopher Tate6785dd82009-06-18 15:58:25 -0700177 } catch (IOException e) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700178 Log.e(TAG, "Unable to write package deletions!");
Christopher Tate6785dd82009-06-18 15:58:25 -0700179 return;
180 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700181 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700182 } catch (IOException e) {
183 // Real error writing data
184 Log.e(TAG, "Unable to write package backup data file!");
185 return;
Christopher Tate6785dd82009-06-18 15:58:25 -0700186 }
187
188 // Finally, write the new state blob -- just the list of all apps we handled
189 writeStateFile(mAllApps, newState);
190 }
191
192 // "Restore" here is a misnomer. What we're really doing is reading back the
193 // set of app signatures associated with each backed-up app in this restore
194 // image. We'll use those later to determine what we can legitimately restore.
195 public void onRestore(BackupDataInput data, ParcelFileDescriptor newState)
196 throws IOException {
197 List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
Christopher Tate6aa41f42009-06-19 14:14:22 -0700198 HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>();
Christopher Tate3a31a932009-06-22 15:10:30 -0700199 if (DEBUG) Log.v(TAG, "onRestore()");
200 int storedSystemVersion = -1;
Christopher Tate6785dd82009-06-18 15:58:25 -0700201
202 while (data.readNextHeader()) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700203 String key = data.getKey();
Christopher Tate6785dd82009-06-18 15:58:25 -0700204 int dataSize = data.getDataSize();
Christopher Tate6785dd82009-06-18 15:58:25 -0700205
Christopher Tate3a31a932009-06-22 15:10:30 -0700206 if (DEBUG) Log.v(TAG, " got key=" + key + " dataSize=" + dataSize);
Christopher Tate6aa41f42009-06-19 14:14:22 -0700207
Christopher Tate3a31a932009-06-22 15:10:30 -0700208 // generic setup to parse any entity data
209 byte[] dataBuf = new byte[dataSize];
210 data.readEntityData(dataBuf, 0, dataSize);
211 ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);
212 DataInputStream in = new DataInputStream(baStream);
213
214 if (key.equals(GLOBAL_METADATA_KEY)) {
215 storedSystemVersion = in.readInt();
216 if (DEBUG) Log.v(TAG, " storedSystemVersion = " + storedSystemVersion);
217 if (storedSystemVersion > Build.VERSION.SDK_INT) {
218 // returning before setting the sig map means we rejected the restore set
219 Log.w(TAG, "Restore set was from a later version of Android; not restoring");
220 return;
221 }
222 // !!! TODO: remove this debugging output
223 if (DEBUG) {
224 Log.i(TAG, "Restore set version " + storedSystemVersion
225 + " is compatible with OS version " + Build.VERSION.SDK_INT);
226 }
227 } else {
228 // it's a file metadata record
229 int versionCode = in.readInt();
230 Signature[] sigs = unflattenSignatureArray(in);
231// !!! TODO: take out this debugging
232 if (DEBUG) {
233 Log.i(TAG, " restored metadata for " + key
234 + " dataSize=" + dataSize
235 + " versionCode=" + versionCode + " sigs=" + sigs);
236 }
237
238 ApplicationInfo app = new ApplicationInfo();
239 app.packageName = key;
240 restoredApps.add(app);
241 sigMap.put(key, new Metadata(versionCode, sigs));
Christopher Tate6785dd82009-06-18 15:58:25 -0700242 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700243 }
244
Christopher Tate3a31a932009-06-22 15:10:30 -0700245 // On successful completion, cache the signature map for the Backup Manager to use
Christopher Tate6785dd82009-06-18 15:58:25 -0700246 mRestoredSignatures = sigMap;
247 }
248
249
250 // Util: convert an array of Signatures into a flattened byte buffer. The
251 // flattened format contains enough info to reconstruct the signature array.
252 private byte[] flattenSignatureArray(Signature[] allSigs) {
253 ByteArrayOutputStream outBuf = new ByteArrayOutputStream();
254 DataOutputStream out = new DataOutputStream(outBuf);
255
256 // build the set of subsidiary buffers
257 try {
258 // first the # of signatures in the array
259 out.writeInt(allSigs.length);
260
261 // then the signatures themselves, length + flattened buffer
262 for (Signature sig : allSigs) {
263 byte[] flat = sig.toByteArray();
264 out.writeInt(flat.length);
265 out.write(flat);
266 }
267 } catch (IOException e) {
268 // very strange; we're writing to memory here. abort.
269 return null;
270 }
271
272 return outBuf.toByteArray();
273 }
274
Christopher Tate6aa41f42009-06-19 14:14:22 -0700275 private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) {
Christopher Tate6785dd82009-06-18 15:58:25 -0700276 Signature[] sigs = null;
277
278 try {
279 int num = in.readInt();
Christopher Tate3a31a932009-06-22 15:10:30 -0700280 Log.v(TAG, " ... unflatten read " + num);
Christopher Tate6785dd82009-06-18 15:58:25 -0700281 sigs = new Signature[num];
282 for (int i = 0; i < num; i++) {
283 int len = in.readInt();
284 byte[] flatSig = new byte[len];
285 in.read(flatSig);
286 sigs[i] = new Signature(flatSig);
287 }
288 } catch (EOFException e) {
289 // clean termination
290 if (sigs == null) {
291 Log.w(TAG, "Empty signature block found");
292 }
293 } catch (IOException e) {
294 Log.d(TAG, "Unable to unflatten sigs");
295 return null;
296 }
297
298 return sigs;
299 }
300
301 // Util: parse out an existing state file into a usable structure
302 private HashSet<String> parseStateFile(ParcelFileDescriptor stateFile) {
303 HashSet<String> set = new HashSet<String>();
304 // The state file is just the list of app names we have stored signatures for
305 FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor());
306 DataInputStream in = new DataInputStream(instream);
307
308 int bufSize = 256;
309 byte[] buf = new byte[bufSize];
310 try {
311 int nameSize = in.readInt();
312 if (bufSize < nameSize) {
313 bufSize = nameSize + 32;
314 buf = new byte[bufSize];
315 }
316 in.read(buf, 0, nameSize);
317 String pkg = new String(buf, 0, nameSize);
318 set.add(pkg);
319 } catch (EOFException eof) {
320 // safe; we're done
321 } catch (IOException e) {
322 // whoops, bad state file. abort.
323 Log.e(TAG, "Unable to read Package Manager state file");
324 return null;
325 }
326 return set;
327 }
328
Christopher Tate3a31a932009-06-22 15:10:30 -0700329 // Util: write out our new backup state file
Christopher Tate6785dd82009-06-18 15:58:25 -0700330 private void writeStateFile(List<ApplicationInfo> apps, ParcelFileDescriptor stateFile) {
331 FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
332 DataOutputStream out = new DataOutputStream(outstream);
333
Christopher Tate3a31a932009-06-22 15:10:30 -0700334 try {
335 // by the time we get here we know we've stored the global metadata record
336 byte[] metaNameBuf = GLOBAL_METADATA_KEY.getBytes();
337 out.writeInt(metaNameBuf.length);
338 out.write(metaNameBuf);
339
340 // now write all the app names too
341 for (ApplicationInfo app : apps) {
Christopher Tate6785dd82009-06-18 15:58:25 -0700342 byte[] pkgNameBuf = app.packageName.getBytes();
343 out.writeInt(pkgNameBuf.length);
344 out.write(pkgNameBuf);
Christopher Tate6785dd82009-06-18 15:58:25 -0700345 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700346 } catch (IOException e) {
347 Log.e(TAG, "Unable to write package manager state file!");
348 return;
Christopher Tate6785dd82009-06-18 15:58:25 -0700349 }
350 }
351}