OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / frameworks / base / services / java / com / android / server / PackageManagerBackupAgent.java
1 /*
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
17 package com.android.server;
18
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;
30
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;
43 import java.util.Set;
44
45 /**
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.
49  *
50  * Since the Package Manager isn't a proper "application" we just provide a
51  * direct IBackupAgent implementation and hand-construct it at need.
52  */
53 public class PackageManagerBackupAgent extends BackupAgent {
54     private static final String TAG = "PMBA";
55     private static final boolean DEBUG = false;
56
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@";
60
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>();
67
68     private final HashSet<String> mExisting = new HashSet<String>();
69     private int mStoredSdkVersion;
70     private String mStoredIncrementalVersion;
71     private boolean mHasMetadata;
72
73     public class Metadata {
74         public int versionCode;
75         public Signature[] signatures;
76
77         Metadata(int version, Signature[] sigs) {
78             versionCode = version;
79             signatures = sigs;
80         }
81     }
82
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;
89         mHasMetadata = false;
90     }
91
92     public boolean hasMetadata() {
93         return mHasMetadata;
94     }
95
96     public Metadata getRestoredMetadata(String packageName) {
97         if (mRestoredSignatures == null) {
98             Slog.w(TAG, "getRestoredMetadata() before metadata read!");
99             return null;
100         }
101
102         return mRestoredSignatures.get(packageName);
103     }
104
105     public Set<String> getRestoredPackages() {
106         if (mRestoredSignatures == null) {
107             Slog.w(TAG, "getRestoredPackages() before metadata read!");
108             return null;
109         }
110
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();
117     }
118     
119     // The backed up data is the signature block for each app, keyed by
120     // the package name.
121     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
122             ParcelFileDescriptor newState) {
123         if (DEBUG) Slog.v(TAG, "onBackup()");
124
125         ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();  // we'll reuse these
126         DataOutputStream outputBufferStream = new DataOutputStream(outputBuffer);
127         parseStateFile(oldState);
128
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");
136             mExisting.clear();
137         }
138
139         try {
140             /*
141              * Global metadata:
142              *
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
147              *                       the backup set.
148              */
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());
154             } else {
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);
158             }
159
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
166                     continue;
167                 } else {
168                     PackageInfo info = null;
169                     try {
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);
176                         continue;
177                     }
178
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) {
187                             continue;
188                         }
189                     }
190                     
191                     if (info.signatures == null || info.signatures.length == 0)
192                     {
193                         Slog.w(TAG, "Not backing up package " + packName
194                                 + " since it appears to have no signatures.");
195                         continue;
196                     }
197
198                     // We need to store this app's metadata
199                     /*
200                      * Metadata for each package:
201                      *
202                      * int version       -- [4] the package's versionCode
203                      * byte[] signatures -- [len] flattened Signature[] of the package
204                      */
205
206                     // marshal the version code in a canonical form
207                     outputBuffer.reset();
208                     outputBufferStream.writeInt(info.versionCode);
209                     writeSignatureArray(outputBufferStream, info.signatures);
210
211                     if (DEBUG) {
212                         Slog.v(TAG, "+ writing metadata for " + packName
213                                 + " version=" + info.versionCode
214                                 + " entityLen=" + outputBuffer.size());
215                     }
216                     
217                     // Now we can write the backup entity for this package
218                     writeEntity(data, packName, outputBuffer.toByteArray());
219                 }
220             }
221
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);
227                 try {
228                     data.writeEntityHeader(app, -1);
229                 } catch (IOException e) {
230                     Slog.e(TAG, "Unable to write package deletions!");
231                     return;
232                 }
233             }
234         } catch (IOException e) {
235             // Real error writing data
236             Slog.e(TAG, "Unable to write package backup data file!");
237             return;
238         }
239
240         // Finally, write the new state blob -- just the list of all apps we handled
241         writeStateFile(mAllPackages, newState);
242     }
243     
244     private static void writeEntity(BackupDataOutput data, String key, byte[] bytes)
245             throws IOException {
246         data.writeEntityHeader(key, bytes.length);
247         data.writeEntityData(bytes, bytes.length);
248     }
249
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)
254             throws IOException {
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;
259
260         while (data.readNextHeader()) {
261             String key = data.getKey();
262             int dataSize = data.getDataSize();
263
264             if (DEBUG) Slog.v(TAG, "   got key=" + key + " dataSize=" + dataSize);
265
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);
271
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");
278                     return;
279                 }
280                 mStoredSdkVersion = storedSdkVersion;
281                 mStoredIncrementalVersion = inputBufferStream.readUTF();
282                 mHasMetadata = true;
283                 if (DEBUG) {
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 + ")");
288                 }
289             } else {
290                 // it's a file metadata record
291                 int versionCode = inputBufferStream.readInt();
292                 Signature[] sigs = readSignatureArray(inputBufferStream);
293                 if (DEBUG) {
294                     Slog.i(TAG, "   read metadata for " + key
295                             + " dataSize=" + dataSize
296                             + " versionCode=" + versionCode + " sigs=" + sigs);
297                 }
298                 
299                 if (sigs == null || sigs.length == 0) {
300                     Slog.w(TAG, "Not restoring package " + key
301                             + " since it appears to have no signatures.");
302                     continue;
303                 }
304
305                 ApplicationInfo app = new ApplicationInfo();
306                 app.packageName = key;
307                 restoredApps.add(app);
308                 sigMap.put(key, new Metadata(versionCode, sigs));
309             }
310         }
311
312         // On successful completion, cache the signature map for the Backup Manager to use
313         mRestoredSignatures = sigMap;
314     }
315
316     private static void writeSignatureArray(DataOutputStream out, Signature[] sigs)
317             throws IOException {
318         // write the number of signatures in the array
319         out.writeInt(sigs.length);
320
321         // write the signatures themselves, length + flattened buffer
322         for (Signature sig : sigs) {
323             byte[] flat = sig.toByteArray();
324             out.writeInt(flat.length);
325             out.write(flat);
326         }
327     }
328
329     private static Signature[] readSignatureArray(DataInputStream in) {
330         try {
331             int num;
332             try {
333                 num = in.readInt();
334             } catch (EOFException e) {
335                 // clean termination
336                 Slog.w(TAG, "Read empty signature block");
337                 return null;
338             }
339             
340             if (DEBUG) Slog.v(TAG, " ... unflatten read " + num);
341             
342             // Sensical?
343             if (num > 20) {
344                 Slog.e(TAG, "Suspiciously large sig count in restore data; aborting");
345                 throw new IllegalStateException("Bad restore state");
346             }
347             
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];
352                 in.read(flatSig);
353                 sigs[i] = new Signature(flatSig);
354             }
355             return sigs;
356         } catch (IOException e) {
357             Slog.e(TAG, "Unable to read signatures");
358             return null;
359         }
360     }
361
362     // Util: parse out an existing state file into a usable structure
363     private void parseStateFile(ParcelFileDescriptor stateFile) {
364         mExisting.clear();
365         mStateVersions.clear();
366         mStoredSdkVersion = 0;
367         mStoredIncrementalVersion = null;
368
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);
375
376         int bufSize = 256;
377         byte[] buf = new byte[bufSize];
378         try {
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);
384             } else {
385                 Slog.e(TAG, "No global metadata in state file!");
386                 return;
387             }
388
389             // The global metadata was first; now read all the apps
390             while (true) {
391                 pkg = in.readUTF();
392                 int versionCode = in.readInt();
393                 mExisting.add(pkg);
394                 mStateVersions.put(pkg, new Metadata(versionCode, null));
395             }
396         } catch (EOFException eof) {
397             // safe; we're done
398         } catch (IOException e) {
399             // whoops, bad state file.  abort.
400             Slog.e(TAG, "Unable to read Package Manager state file: " + e);
401         }
402     }
403
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);
408
409         try {
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);
414
415             // now write all the app names too
416             for (PackageInfo pkg : pkgs) {
417                 out.writeUTF(pkg.packageName);
418                 out.writeInt(pkg.versionCode);
419             }
420         } catch (IOException e) {
421             Slog.e(TAG, "Unable to write package manager state file!");
422             return;
423         }
424     }
425 }