blob: 6bd32a02f464a822913f538e13941dd3dabbbf7b [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;
27import android.os.ParcelFileDescriptor;
28import android.util.Log;
29
30import java.io.ByteArrayInputStream;
31import java.io.ByteArrayOutputStream;
32import java.io.DataInputStream;
33import java.io.DataOutputStream;
34import java.io.EOFException;
35import java.io.FileInputStream;
36import java.io.FileOutputStream;
37import java.io.IOException;
38import java.util.ArrayList;
39import java.util.HashMap;
40import java.util.HashSet;
41import java.util.List;
42
43// !!!TODO: take this out
44import java.util.zip.CRC32;
45
46/**
47 * We back up the signatures of each package so that during a system restore,
48 * we can verify that the app whose data we think we have matches the app
49 * actually resident on the device.
50 *
51 * Since the Package Manager isn't a proper "application" we just provide a
52 * direct IBackupAgent implementation and hand-construct it at need.
53 */
54public class PackageManagerBackupAgent extends BackupAgent {
55 private static final String TAG = "PMBA";
56 private static final boolean DEBUG = true;
57
58 private List<ApplicationInfo> mAllApps;
59 private PackageManager mPackageManager;
60 private HashMap<String, Signature[]> mRestoredSignatures;
61
62 // We're constructed with the set of applications that are participating
63 // in backup. This set changes as apps are installed & removed.
64 PackageManagerBackupAgent(PackageManager packageMgr, List<ApplicationInfo> apps) {
65 mPackageManager = packageMgr;
66 mAllApps = apps;
67 mRestoredSignatures = null;
68 }
69
70 public Signature[] getRestoredSignatures(String packageName) {
71 if (mRestoredSignatures == null) {
72 return null;
73 }
74
75 return mRestoredSignatures.get(packageName);
76 }
77
78 // The backed up data is the signature block for each app, keyed by
79 // the package name.
80 public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
81 ParcelFileDescriptor newState) {
82 HashSet<String> existing = parseStateFile(oldState);
83
84 // For each app we have on device, see if we've backed it up yet. If not,
85 // write its signature block to the output, keyed on the package name.
86 for (ApplicationInfo app : mAllApps) {
87 String packName = app.packageName;
88 if (!existing.contains(packName)) {
89 // We haven't stored this app's signatures yet, so we do that now
90 try {
91 PackageInfo info = mPackageManager.getPackageInfo(packName,
92 PackageManager.GET_SIGNATURES);
93 // build a byte array out of the signature list
94 byte[] sigs = flattenSignatureArray(info.signatures);
95// !!! TODO: take out this debugging
96 if (DEBUG) {
97 CRC32 crc = new CRC32();
98 crc.update(sigs);
99 Log.i(TAG, "+ flat sig array for " + packName + " : "
100 + crc.getValue());
101 }
102 data.writeEntityHeader(packName, sigs.length);
103 data.writeEntityData(sigs, sigs.length);
104 } catch (NameNotFoundException e) {
105 // Weird; we just found it, and now are told it doesn't exist.
106 // Treat it as having been removed from the device.
107 existing.add(packName);
108 } catch (IOException e) {
109 // Real error writing data
110 Log.e(TAG, "Unable to write package backup data file!");
111 return;
112 }
113 } else {
114 // We've already backed up this app. Remove it from the set so
115 // we can tell at the end what has disappeared from the device.
116 if (!existing.remove(packName)) {
117 Log.d(TAG, "*** failed to remove " + packName + " from package set!");
118 }
119 }
120 }
121
122 // At this point, the only entries in 'existing' are apps that were
123 // mentioned in the saved state file, but appear to no longer be present
124 // on the device. Write a deletion entity for them.
125 for (String app : existing) {
126 try {
127 data.writeEntityHeader(app, -1);
128 } catch (IOException e) {
129 Log.e(TAG, "Unable to write package deletions!");
130 return;
131 }
132 }
133
134 // Finally, write the new state blob -- just the list of all apps we handled
135 writeStateFile(mAllApps, newState);
136 }
137
138 // "Restore" here is a misnomer. What we're really doing is reading back the
139 // set of app signatures associated with each backed-up app in this restore
140 // image. We'll use those later to determine what we can legitimately restore.
141 public void onRestore(BackupDataInput data, ParcelFileDescriptor newState)
142 throws IOException {
143 List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
144 HashMap<String, Signature[]> sigMap = new HashMap<String, Signature[]>();
145
146 while (data.readNextHeader()) {
147 int dataSize = data.getDataSize();
148 byte[] buf = new byte[dataSize];
149 data.readEntityData(buf, 0, dataSize);
150
151 Signature[] sigs = unflattenSignatureArray(buf);
152 String pkg = data.getKey();
153// !!! TODO: take out this debugging
154 if (DEBUG) {
155 CRC32 crc = new CRC32();
156 crc.update(buf);
157 Log.i(TAG, "- unflat sig array for " + pkg + " : "
158 + crc.getValue());
159 }
160
161 ApplicationInfo app = new ApplicationInfo();
162 app.packageName = pkg;
163 restoredApps.add(app);
164 sigMap.put(pkg, sigs);
165 }
166
167 mRestoredSignatures = sigMap;
168 }
169
170
171 // Util: convert an array of Signatures into a flattened byte buffer. The
172 // flattened format contains enough info to reconstruct the signature array.
173 private byte[] flattenSignatureArray(Signature[] allSigs) {
174 ByteArrayOutputStream outBuf = new ByteArrayOutputStream();
175 DataOutputStream out = new DataOutputStream(outBuf);
176
177 // build the set of subsidiary buffers
178 try {
179 // first the # of signatures in the array
180 out.writeInt(allSigs.length);
181
182 // then the signatures themselves, length + flattened buffer
183 for (Signature sig : allSigs) {
184 byte[] flat = sig.toByteArray();
185 out.writeInt(flat.length);
186 out.write(flat);
187 }
188 } catch (IOException e) {
189 // very strange; we're writing to memory here. abort.
190 return null;
191 }
192
193 return outBuf.toByteArray();
194 }
195
196 private Signature[] unflattenSignatureArray(byte[] buffer) {
197 ByteArrayInputStream inBufStream = new ByteArrayInputStream(buffer);
198 DataInputStream in = new DataInputStream(inBufStream);
199 Signature[] sigs = null;
200
201 try {
202 int num = in.readInt();
203 sigs = new Signature[num];
204 for (int i = 0; i < num; i++) {
205 int len = in.readInt();
206 byte[] flatSig = new byte[len];
207 in.read(flatSig);
208 sigs[i] = new Signature(flatSig);
209 }
210 } catch (EOFException e) {
211 // clean termination
212 if (sigs == null) {
213 Log.w(TAG, "Empty signature block found");
214 }
215 } catch (IOException e) {
216 Log.d(TAG, "Unable to unflatten sigs");
217 return null;
218 }
219
220 return sigs;
221 }
222
223 // Util: parse out an existing state file into a usable structure
224 private HashSet<String> parseStateFile(ParcelFileDescriptor stateFile) {
225 HashSet<String> set = new HashSet<String>();
226 // The state file is just the list of app names we have stored signatures for
227 FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor());
228 DataInputStream in = new DataInputStream(instream);
229
230 int bufSize = 256;
231 byte[] buf = new byte[bufSize];
232 try {
233 int nameSize = in.readInt();
234 if (bufSize < nameSize) {
235 bufSize = nameSize + 32;
236 buf = new byte[bufSize];
237 }
238 in.read(buf, 0, nameSize);
239 String pkg = new String(buf, 0, nameSize);
240 set.add(pkg);
241 } catch (EOFException eof) {
242 // safe; we're done
243 } catch (IOException e) {
244 // whoops, bad state file. abort.
245 Log.e(TAG, "Unable to read Package Manager state file");
246 return null;
247 }
248 return set;
249 }
250
251 // Util: write a set of names into a new state file
252 private void writeStateFile(List<ApplicationInfo> apps, ParcelFileDescriptor stateFile) {
253 FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
254 DataOutputStream out = new DataOutputStream(outstream);
255
256 for (ApplicationInfo app : apps) {
257 try {
258 byte[] pkgNameBuf = app.packageName.getBytes();
259 out.writeInt(pkgNameBuf.length);
260 out.write(pkgNameBuf);
261 } catch (IOException e) {
262 Log.e(TAG, "Unable to write package manager state file!");
263 return;
264 }
265 }
266 }
267}