blob: 0fbbb3fb06d0d54770ed52c5c2e15726af54dfc1 [file] [log] [blame]
Christopher Tate9bbc21a2009-06-10 20:23:25 -07001package com.android.internal.backup;
2
Christopher Tate2fdd4282009-06-12 15:20:04 -07003import android.backup.BackupDataInput;
Christopher Tate8e55eac92009-06-15 12:31:20 -07004import android.backup.BackupDataOutput;
Christopher Tate9bbc21a2009-06-10 20:23:25 -07005import android.backup.RestoreSet;
6import android.content.Context;
7import android.content.pm.PackageInfo;
8import android.content.pm.PackageManager;
9import android.content.pm.PackageManager.NameNotFoundException;
10import android.os.Environment;
11import android.os.ParcelFileDescriptor;
12import android.os.RemoteException;
13import android.util.Log;
14
Christopher Tatee9190a22009-06-17 17:52:05 -070015import org.bouncycastle.util.encoders.Base64;
16
Christopher Tate9bbc21a2009-06-10 20:23:25 -070017import java.io.File;
18import java.io.FileFilter;
19import java.io.FileInputStream;
20import java.io.FileOutputStream;
21import java.io.IOException;
22import java.util.ArrayList;
23
24/**
25 * Backup transport for stashing stuff into a known location on disk, and
26 * later restoring from there. For testing only.
27 */
28
29public class LocalTransport extends IBackupTransport.Stub {
30 private static final String TAG = "LocalTransport";
Christopher Tate2fdd4282009-06-12 15:20:04 -070031 private static final boolean DEBUG = true;
Christopher Tate9bbc21a2009-06-10 20:23:25 -070032
33 private Context mContext;
34 private PackageManager mPackageManager;
35 private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
Dan Egnorefe52642009-06-24 00:16:33 -070036 private PackageInfo[] mRestorePackages = null;
37 private int mRestorePackage = -1; // Index into mRestorePackages
Christopher Tate9bbc21a2009-06-10 20:23:25 -070038
39
40 public LocalTransport(Context context) {
Christopher Tate2fdd4282009-06-12 15:20:04 -070041 if (DEBUG) Log.v(TAG, "Transport constructed");
Christopher Tate9bbc21a2009-06-10 20:23:25 -070042 mContext = context;
43 mPackageManager = context.getPackageManager();
44 }
45
46 public long requestBackupTime() throws RemoteException {
47 // any time is a good time for local backup
48 return 0;
49 }
50
Dan Egnorefe52642009-06-24 00:16:33 -070051 public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
Christopher Tate9bbc21a2009-06-10 20:23:25 -070052 throws RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -070053 if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
Christopher Tate2fdd4282009-06-12 15:20:04 -070054
Christopher Tate9bbc21a2009-06-10 20:23:25 -070055 File packageDir = new File(mDataDir, packageInfo.packageName);
Christopher Tate2fdd4282009-06-12 15:20:04 -070056 packageDir.mkdirs();
Christopher Tate9bbc21a2009-06-10 20:23:25 -070057
Christopher Tate2fdd4282009-06-12 15:20:04 -070058 // Each 'record' in the restore set is kept in its own file, named by
59 // the record key. Wind through the data file, extracting individual
60 // record operations and building a set of all the updates to apply
61 // in this update.
62 BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor());
63 try {
64 int bufSize = 512;
65 byte[] buf = new byte[bufSize];
66 while (changeSet.readNextHeader()) {
67 String key = changeSet.getKey();
Joe Onorato5d605dc2009-06-18 18:23:43 -070068 String base64Key = new String(Base64.encode(key.getBytes()));
69 File entityFile = new File(packageDir, base64Key);
70
Christopher Tate2fdd4282009-06-12 15:20:04 -070071 int dataSize = changeSet.getDataSize();
Christopher Tatee9190a22009-06-17 17:52:05 -070072
Christopher Tatee9190a22009-06-17 17:52:05 -070073 if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize
74 + " key64=" + base64Key);
Christopher Tate9bbc21a2009-06-10 20:23:25 -070075
Joe Onorato5d605dc2009-06-18 18:23:43 -070076 if (dataSize >= 0) {
77 FileOutputStream entity = new FileOutputStream(entityFile);
78
79 if (dataSize > bufSize) {
80 bufSize = dataSize;
81 buf = new byte[bufSize];
82 }
83 changeSet.readEntityData(buf, 0, dataSize);
84 if (DEBUG) Log.v(TAG, " data size " + dataSize);
85
86 try {
87 entity.write(buf, 0, dataSize);
88 } catch (IOException e) {
Dan Egnorefe52642009-06-24 00:16:33 -070089 Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
90 return false;
Joe Onorato5d605dc2009-06-18 18:23:43 -070091 } finally {
92 entity.close();
93 }
94 } else {
95 entityFile.delete();
Christopher Tate2fdd4282009-06-12 15:20:04 -070096 }
97 }
Dan Egnorefe52642009-06-24 00:16:33 -070098 return true;
Christopher Tate2fdd4282009-06-12 15:20:04 -070099 } catch (IOException e) {
100 // oops, something went wrong. abort the operation and return error.
Dan Egnorefe52642009-06-24 00:16:33 -0700101 Log.v(TAG, "Exception reading backup input:", e);
102 return false;
Christopher Tate2fdd4282009-06-12 15:20:04 -0700103 }
Dan Egnorefe52642009-06-24 00:16:33 -0700104 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700105
Dan Egnorefe52642009-06-24 00:16:33 -0700106 public boolean finishBackup() throws RemoteException {
107 if (DEBUG) Log.v(TAG, "finishBackup()");
108 return true;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700109 }
110
111 // Restore handling
112 public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
113 // one hardcoded restore set
Christopher Tatef68eb502009-06-16 11:02:01 -0700114 RestoreSet set = new RestoreSet("Local disk image", "flash", 0);
115 RestoreSet[] array = { set };
116 return array;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700117 }
118
Dan Egnorefe52642009-06-24 00:16:33 -0700119 public boolean startRestore(long token, PackageInfo[] packages) {
120 if (DEBUG) Log.v(TAG, "start restore " + token);
121 mRestorePackages = packages;
122 mRestorePackage = -1;
123 return true;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700124 }
125
Dan Egnorefe52642009-06-24 00:16:33 -0700126 public String nextRestorePackage() {
127 if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
128 while (++mRestorePackage < mRestorePackages.length) {
129 String name = mRestorePackages[mRestorePackage].packageName;
130 if (new File(mDataDir, name).isDirectory()) {
131 if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name);
132 return name;
133 }
134 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700135
Dan Egnorefe52642009-06-24 00:16:33 -0700136 if (DEBUG) Log.v(TAG, " no more packages to restore");
137 return "";
138 }
139
140 public boolean getRestoreData(ParcelFileDescriptor outFd) {
141 if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
142 if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
143 File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName);
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700144
Christopher Tate2fdd4282009-06-12 15:20:04 -0700145 // The restore set is the concatenation of the individual record blobs,
146 // each of which is a file in the package's directory
147 File[] blobs = packageDir.listFiles();
Dan Egnorefe52642009-06-24 00:16:33 -0700148 if (blobs == null) {
149 Log.e(TAG, "Error listing directory: " + packageDir);
150 return false; // nextRestorePackage() ensures the dir exists, so this is an error
Christopher Tate2fdd4282009-06-12 15:20:04 -0700151 }
Dan Egnorefe52642009-06-24 00:16:33 -0700152
153 // We expect at least some data if the directory exists in the first place
154 if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.length + " key files");
155 BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
156 try {
157 for (File f : blobs) {
158 FileInputStream in = new FileInputStream(f);
159 try {
160 int size = (int) f.length();
161 byte[] buf = new byte[size];
162 in.read(buf);
163 String key = new String(Base64.decode(f.getName()));
164 if (DEBUG) Log.v(TAG, " ... key=" + key + " size=" + size);
165 out.writeEntityHeader(key, size);
166 out.writeEntityData(buf, size);
167 } finally {
168 in.close();
169 }
170 }
171 return true;
172 } catch (IOException e) {
173 Log.e(TAG, "Unable to read backup records", e);
174 return false;
175 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700176 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700177
Dan Egnorefe52642009-06-24 00:16:33 -0700178 public void finishRestore() {
179 if (DEBUG) Log.v(TAG, "finishRestore()");
Christopher Tate3a31a932009-06-22 15:10:30 -0700180 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700181}