blob: 77bddb0717ec56ac16ac0d8decc954cc231ce2e9 [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
Christopher Tate45281862010-03-05 15:46:30 -080019import android.app.backup.BackupAgent;
20import android.app.backup.BackupDataInput;
21import android.app.backup.BackupDataOutput;
Christopher Tate6785dd82009-06-18 15:58:25 -070022import 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;
Joe Onorato8a9b2202010-02-26 18:56:32 -080029import android.util.Slog;
Christopher Tate6785dd82009-06-18 15:58:25 -070030
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;
Christopher Tateb49ceb32010-02-08 16:22:24 -080043import java.util.Set;
Christopher Tate6785dd82009-06-18 15:58:25 -070044
Christopher Tate6785dd82009-06-18 15:58:25 -070045/**
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 */
53public class PackageManagerBackupAgent extends BackupAgent {
54 private static final String TAG = "PMBA";
Christopher Tateb808a9392009-09-29 16:09:52 -070055 private static final boolean DEBUG = false;
Christopher Tate6785dd82009-06-18 15:58:25 -070056
Christopher Tate3a31a932009-06-22 15:10:30 -070057 // 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
Dan Egnorefe52642009-06-24 00:16:33 -070061 private List<PackageInfo> mAllPackages;
Christopher Tate6785dd82009-06-18 15:58:25 -070062 private PackageManager mPackageManager;
Christopher Tate72d19aa2009-06-30 12:47:33 -070063 // version & signature info of each app in a restore set
Christopher Tate6aa41f42009-06-19 14:14:22 -070064 private HashMap<String, Metadata> mRestoredSignatures;
Christopher Tate72d19aa2009-06-30 12:47:33 -070065 // 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;
Christopher Tate3d7cd132009-07-07 14:23:07 -070071 private boolean mHasMetadata;
Christopher Tate6aa41f42009-06-19 14:14:22 -070072
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 }
Christopher Tate6785dd82009-06-18 15:58:25 -070082
83 // We're constructed with the set of applications that are participating
84 // in backup. This set changes as apps are installed & removed.
Dan Egnorefe52642009-06-24 00:16:33 -070085 PackageManagerBackupAgent(PackageManager packageMgr, List<PackageInfo> packages) {
Christopher Tate6785dd82009-06-18 15:58:25 -070086 mPackageManager = packageMgr;
Dan Egnorefe52642009-06-24 00:16:33 -070087 mAllPackages = packages;
Christopher Tate6785dd82009-06-18 15:58:25 -070088 mRestoredSignatures = null;
Christopher Tate3d7cd132009-07-07 14:23:07 -070089 mHasMetadata = false;
90 }
91
92 public boolean hasMetadata() {
93 return mHasMetadata;
Christopher Tate6785dd82009-06-18 15:58:25 -070094 }
95
Christopher Tate6aa41f42009-06-19 14:14:22 -070096 public Metadata getRestoredMetadata(String packageName) {
Christopher Tate6785dd82009-06-18 15:58:25 -070097 if (mRestoredSignatures == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -080098 Slog.w(TAG, "getRestoredMetadata() before metadata read!");
Christopher Tate6785dd82009-06-18 15:58:25 -070099 return null;
100 }
101
102 return mRestoredSignatures.get(packageName);
103 }
Christopher Tateb49ceb32010-02-08 16:22:24 -0800104
105 public Set<String> getRestoredPackages() {
106 if (mRestoredSignatures == null) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800107 Slog.w(TAG, "getRestoredPackages() before metadata read!");
Christopher Tateb49ceb32010-02-08 16:22:24 -0800108 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 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700118
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) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800123 if (DEBUG) Slog.v(TAG, "onBackup()");
Christopher Tate3a31a932009-06-22 15:10:30 -0700124
Jeff Browne684d952010-04-05 17:42:10 -0700125 ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream(); // we'll reuse these
126 DataOutputStream outputBufferStream = new DataOutputStream(outputBuffer);
Christopher Tate72d19aa2009-06-30 12:47:33 -0700127 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)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800134 Slog.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs "
Christopher Tate72d19aa2009-06-30 12:47:33 -0700135 + Build.VERSION.INCREMENTAL + " - rewriting");
136 mExisting.clear();
137 }
Christopher Tate6aa41f42009-06-19 14:14:22 -0700138
Christopher Tate3a31a932009-06-22 15:10:30 -0700139 try {
140 /*
141 * Global metadata:
142 *
Christopher Tate72d19aa2009-06-30 12:47:33 -0700143 * 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.
Christopher Tate3a31a932009-06-22 15:10:30 -0700148 */
Christopher Tate72d19aa2009-06-30 12:47:33 -0700149 if (!mExisting.contains(GLOBAL_METADATA_KEY)) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800150 if (DEBUG) Slog.v(TAG, "Storing global metadata key");
Jeff Browne684d952010-04-05 17:42:10 -0700151 outputBufferStream.writeInt(Build.VERSION.SDK_INT);
152 outputBufferStream.writeUTF(Build.VERSION.INCREMENTAL);
153 writeEntity(data, GLOBAL_METADATA_KEY, outputBuffer.toByteArray());
Christopher Tate3a31a932009-06-22 15:10:30 -0700154 } else {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800155 if (DEBUG) Slog.v(TAG, "Global metadata key already stored");
Christopher Tate72d19aa2009-06-30 12:47:33 -0700156 // don't consider it to have been skipped/deleted
157 mExisting.remove(GLOBAL_METADATA_KEY);
Christopher Tate3a31a932009-06-22 15:10:30 -0700158 }
Christopher Tate6aa41f42009-06-19 14:14:22 -0700159
Christopher Tate3a31a932009-06-22 15:10:30 -0700160 // 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.
Dan Egnorefe52642009-06-24 00:16:33 -0700162 for (PackageInfo pkg : mAllPackages) {
163 String packName = pkg.packageName;
Christopher Tate6f317422009-06-29 18:52:55 -0700164 if (packName.equals(GLOBAL_METADATA_KEY)) {
165 // We've already handled the metadata key; skip it here
166 continue;
Christopher Tate72d19aa2009-06-30 12:47:33 -0700167 } else {
168 PackageInfo info = null;
Christopher Tate3a31a932009-06-22 15:10:30 -0700169 try {
Christopher Tate72d19aa2009-06-30 12:47:33 -0700170 info = mPackageManager.getPackageInfo(packName,
Christopher Tate3a31a932009-06-22 15:10:30 -0700171 PackageManager.GET_SIGNATURES);
Christopher Tate72d19aa2009-06-30 12:47:33 -0700172 } 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
Jeff Browne684d952010-04-05 17:42:10 -0700179 if (mExisting.contains(packName)) {
180 // We have backed up this app before. Check whether the version
Christopher Tate72d19aa2009-06-30 12:47:33 -0700181 // 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.
Christopher Tate72d19aa2009-06-30 12:47:33 -0700185 mExisting.remove(packName);
Jeff Browne684d952010-04-05 17:42:10 -0700186 if (info.versionCode == mStateVersions.get(packName).versionCode) {
187 continue;
Christopher Tate3a31a932009-06-22 15:10:30 -0700188 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700189 }
Jeff Browne684d952010-04-05 17:42:10 -0700190
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());
Christopher Tate3a31a932009-06-22 15:10:30 -0700219 }
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.
Christopher Tate72d19aa2009-06-30 12:47:33 -0700225 for (String app : mExisting) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800226 if (DEBUG) Slog.v(TAG, "- removing metadata for deleted pkg " + app);
Christopher Tate3a31a932009-06-22 15:10:30 -0700227 try {
228 data.writeEntityHeader(app, -1);
Christopher Tate6785dd82009-06-18 15:58:25 -0700229 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800230 Slog.e(TAG, "Unable to write package deletions!");
Christopher Tate6785dd82009-06-18 15:58:25 -0700231 return;
232 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700233 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700234 } catch (IOException e) {
235 // Real error writing data
Joe Onorato8a9b2202010-02-26 18:56:32 -0800236 Slog.e(TAG, "Unable to write package backup data file!");
Christopher Tate3a31a932009-06-22 15:10:30 -0700237 return;
Christopher Tate6785dd82009-06-18 15:58:25 -0700238 }
239
240 // Finally, write the new state blob -- just the list of all apps we handled
Dan Egnorefe52642009-06-24 00:16:33 -0700241 writeStateFile(mAllPackages, newState);
Christopher Tate6785dd82009-06-18 15:58:25 -0700242 }
Jeff Browne684d952010-04-05 17:42:10 -0700243
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 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700249
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.
Christopher Tate5cbbf562009-06-22 16:44:51 -0700253 public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
Christopher Tate6785dd82009-06-18 15:58:25 -0700254 throws IOException {
255 List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>();
Christopher Tate6aa41f42009-06-19 14:14:22 -0700256 HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800257 if (DEBUG) Slog.v(TAG, "onRestore()");
Christopher Tate3a31a932009-06-22 15:10:30 -0700258 int storedSystemVersion = -1;
Christopher Tate6785dd82009-06-18 15:58:25 -0700259
260 while (data.readNextHeader()) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700261 String key = data.getKey();
Christopher Tate6785dd82009-06-18 15:58:25 -0700262 int dataSize = data.getDataSize();
Christopher Tate6785dd82009-06-18 15:58:25 -0700263
Joe Onorato8a9b2202010-02-26 18:56:32 -0800264 if (DEBUG) Slog.v(TAG, " got key=" + key + " dataSize=" + dataSize);
Christopher Tate6aa41f42009-06-19 14:14:22 -0700265
Christopher Tate3a31a932009-06-22 15:10:30 -0700266 // generic setup to parse any entity data
Jeff Browne684d952010-04-05 17:42:10 -0700267 byte[] inputBytes = new byte[dataSize];
268 data.readEntityData(inputBytes, 0, dataSize);
269 ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes);
270 DataInputStream inputBufferStream = new DataInputStream(inputBuffer);
Christopher Tate3a31a932009-06-22 15:10:30 -0700271
272 if (key.equals(GLOBAL_METADATA_KEY)) {
Jeff Browne684d952010-04-05 17:42:10 -0700273 int storedSdkVersion = inputBufferStream.readInt();
Joe Onorato8a9b2202010-02-26 18:56:32 -0800274 if (DEBUG) Slog.v(TAG, " storedSystemVersion = " + storedSystemVersion);
Christopher Tate3a31a932009-06-22 15:10:30 -0700275 if (storedSystemVersion > Build.VERSION.SDK_INT) {
276 // returning before setting the sig map means we rejected the restore set
Joe Onorato8a9b2202010-02-26 18:56:32 -0800277 Slog.w(TAG, "Restore set was from a later version of Android; not restoring");
Christopher Tate3a31a932009-06-22 15:10:30 -0700278 return;
279 }
Christopher Tate72d19aa2009-06-30 12:47:33 -0700280 mStoredSdkVersion = storedSdkVersion;
Jeff Browne684d952010-04-05 17:42:10 -0700281 mStoredIncrementalVersion = inputBufferStream.readUTF();
Christopher Tate3d7cd132009-07-07 14:23:07 -0700282 mHasMetadata = true;
Christopher Tate3a31a932009-06-22 15:10:30 -0700283 if (DEBUG) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800284 Slog.i(TAG, "Restore set version " + storedSystemVersion
Christopher Tate72d19aa2009-06-30 12:47:33 -0700285 + " is compatible with OS version " + Build.VERSION.SDK_INT
286 + " (" + mStoredIncrementalVersion + " vs "
287 + Build.VERSION.INCREMENTAL + ")");
Christopher Tate3a31a932009-06-22 15:10:30 -0700288 }
289 } else {
290 // it's a file metadata record
Jeff Browne684d952010-04-05 17:42:10 -0700291 int versionCode = inputBufferStream.readInt();
292 Signature[] sigs = readSignatureArray(inputBufferStream);
Christopher Tate3a31a932009-06-22 15:10:30 -0700293 if (DEBUG) {
Jeff Browne684d952010-04-05 17:42:10 -0700294 Slog.i(TAG, " read metadata for " + key
Christopher Tate3a31a932009-06-22 15:10:30 -0700295 + " dataSize=" + dataSize
296 + " versionCode=" + versionCode + " sigs=" + sigs);
297 }
Jeff Browne684d952010-04-05 17:42:10 -0700298
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 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700304
305 ApplicationInfo app = new ApplicationInfo();
306 app.packageName = key;
307 restoredApps.add(app);
308 sigMap.put(key, new Metadata(versionCode, sigs));
Christopher Tate6785dd82009-06-18 15:58:25 -0700309 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700310 }
311
Christopher Tate3a31a932009-06-22 15:10:30 -0700312 // On successful completion, cache the signature map for the Backup Manager to use
Christopher Tate6785dd82009-06-18 15:58:25 -0700313 mRestoredSignatures = sigMap;
314 }
315
Jeff Browne684d952010-04-05 17:42:10 -0700316 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);
Christopher Tate6785dd82009-06-18 15:58:25 -0700320
Jeff Browne684d952010-04-05 17:42:10 -0700321 // 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);
Christopher Tate6785dd82009-06-18 15:58:25 -0700326 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700327 }
328
Jeff Browne684d952010-04-05 17:42:10 -0700329 private static Signature[] readSignatureArray(DataInputStream in) {
Christopher Tate6785dd82009-06-18 15:58:25 -0700330 try {
Jeff Browne684d952010-04-05 17:42:10 -0700331 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
Joe Onorato8a9b2202010-02-26 18:56:32 -0800340 if (DEBUG) Slog.v(TAG, " ... unflatten read " + num);
Jeff Browne684d952010-04-05 17:42:10 -0700341
Christopher Tate5a8a1152009-09-10 16:08:47 -0700342 // Sensical?
343 if (num > 20) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800344 Slog.e(TAG, "Suspiciously large sig count in restore data; aborting");
Christopher Tate5a8a1152009-09-10 16:08:47 -0700345 throw new IllegalStateException("Bad restore state");
346 }
Jeff Browne684d952010-04-05 17:42:10 -0700347
348 Signature[] sigs = new Signature[num];
Christopher Tate6785dd82009-06-18 15:58:25 -0700349 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 }
Jeff Browne684d952010-04-05 17:42:10 -0700355 return sigs;
Christopher Tate6785dd82009-06-18 15:58:25 -0700356 } catch (IOException e) {
Jeff Browne684d952010-04-05 17:42:10 -0700357 Slog.e(TAG, "Unable to read signatures");
Christopher Tate6785dd82009-06-18 15:58:25 -0700358 return null;
359 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700360 }
361
362 // Util: parse out an existing state file into a usable structure
Christopher Tate72d19aa2009-06-30 12:47:33 -0700363 private void parseStateFile(ParcelFileDescriptor stateFile) {
364 mExisting.clear();
365 mStateVersions.clear();
366 mStoredSdkVersion = 0;
367 mStoredIncrementalVersion = null;
368
Christopher Tate6785dd82009-06-18 15:58:25 -0700369 // The state file is just the list of app names we have stored signatures for
Christopher Tate72d19aa2009-06-30 12:47:33 -0700370 // 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.
Christopher Tate6785dd82009-06-18 15:58:25 -0700373 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 {
Christopher Tate72d19aa2009-06-30 12:47:33 -0700379 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 {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800385 Slog.e(TAG, "No global metadata in state file!");
Christopher Tate72d19aa2009-06-30 12:47:33 -0700386 return;
Christopher Tate6785dd82009-06-18 15:58:25 -0700387 }
Christopher Tate72d19aa2009-06-30 12:47:33 -0700388
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 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700396 } catch (EOFException eof) {
397 // safe; we're done
398 } catch (IOException e) {
399 // whoops, bad state file. abort.
Joe Onorato8a9b2202010-02-26 18:56:32 -0800400 Slog.e(TAG, "Unable to read Package Manager state file: " + e);
Christopher Tate6785dd82009-06-18 15:58:25 -0700401 }
Christopher Tate6785dd82009-06-18 15:58:25 -0700402 }
403
Christopher Tate3a31a932009-06-22 15:10:30 -0700404 // Util: write out our new backup state file
Dan Egnorefe52642009-06-24 00:16:33 -0700405 private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
Christopher Tate6785dd82009-06-18 15:58:25 -0700406 FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
407 DataOutputStream out = new DataOutputStream(outstream);
408
Christopher Tate3a31a932009-06-22 15:10:30 -0700409 try {
410 // by the time we get here we know we've stored the global metadata record
Christopher Tate72d19aa2009-06-30 12:47:33 -0700411 out.writeUTF(GLOBAL_METADATA_KEY);
412 out.writeInt(Build.VERSION.SDK_INT);
413 out.writeUTF(Build.VERSION.INCREMENTAL);
Christopher Tate3a31a932009-06-22 15:10:30 -0700414
415 // now write all the app names too
Dan Egnorefe52642009-06-24 00:16:33 -0700416 for (PackageInfo pkg : pkgs) {
Christopher Tate72d19aa2009-06-30 12:47:33 -0700417 out.writeUTF(pkg.packageName);
418 out.writeInt(pkg.versionCode);
Christopher Tate6785dd82009-06-18 15:58:25 -0700419 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700420 } catch (IOException e) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800421 Slog.e(TAG, "Unable to write package manager state file!");
Christopher Tate3a31a932009-06-22 15:10:30 -0700422 return;
Christopher Tate6785dd82009-06-18 15:58:25 -0700423 }
424 }
425}