blob: c5d9d403a75a0ca86eacf06e6f2b3d38df2a072d [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
Christopher Tate5cb400b2009-06-25 16:03:14 -070033 private static final String TRANSPORT_DIR_NAME
34 = "com.android.internal.backup.LocalTransport";
35
Christopher Tate9bbc21a2009-06-10 20:23:25 -070036 private Context mContext;
37 private PackageManager mPackageManager;
38 private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
Dan Egnorefe52642009-06-24 00:16:33 -070039 private PackageInfo[] mRestorePackages = null;
40 private int mRestorePackage = -1; // Index into mRestorePackages
Christopher Tate9bbc21a2009-06-10 20:23:25 -070041
42
43 public LocalTransport(Context context) {
Christopher Tate2fdd4282009-06-12 15:20:04 -070044 if (DEBUG) Log.v(TAG, "Transport constructed");
Christopher Tate9bbc21a2009-06-10 20:23:25 -070045 mContext = context;
46 mPackageManager = context.getPackageManager();
47 }
48
Christopher Tate5cb400b2009-06-25 16:03:14 -070049
50 public String transportDirName() throws RemoteException {
51 return TRANSPORT_DIR_NAME;
52 }
53
Christopher Tate9bbc21a2009-06-10 20:23:25 -070054 public long requestBackupTime() throws RemoteException {
55 // any time is a good time for local backup
56 return 0;
57 }
58
Dan Egnorefe52642009-06-24 00:16:33 -070059 public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
Christopher Tate9bbc21a2009-06-10 20:23:25 -070060 throws RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -070061 if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
Christopher Tate2fdd4282009-06-12 15:20:04 -070062
Christopher Tate9bbc21a2009-06-10 20:23:25 -070063 File packageDir = new File(mDataDir, packageInfo.packageName);
Christopher Tate2fdd4282009-06-12 15:20:04 -070064 packageDir.mkdirs();
Christopher Tate9bbc21a2009-06-10 20:23:25 -070065
Christopher Tate2fdd4282009-06-12 15:20:04 -070066 // Each 'record' in the restore set is kept in its own file, named by
67 // the record key. Wind through the data file, extracting individual
68 // record operations and building a set of all the updates to apply
69 // in this update.
70 BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor());
71 try {
72 int bufSize = 512;
73 byte[] buf = new byte[bufSize];
74 while (changeSet.readNextHeader()) {
75 String key = changeSet.getKey();
Joe Onorato5d605dc2009-06-18 18:23:43 -070076 String base64Key = new String(Base64.encode(key.getBytes()));
77 File entityFile = new File(packageDir, base64Key);
78
Christopher Tate2fdd4282009-06-12 15:20:04 -070079 int dataSize = changeSet.getDataSize();
Christopher Tatee9190a22009-06-17 17:52:05 -070080
Christopher Tatee9190a22009-06-17 17:52:05 -070081 if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize
82 + " key64=" + base64Key);
Christopher Tate9bbc21a2009-06-10 20:23:25 -070083
Joe Onorato5d605dc2009-06-18 18:23:43 -070084 if (dataSize >= 0) {
85 FileOutputStream entity = new FileOutputStream(entityFile);
86
87 if (dataSize > bufSize) {
88 bufSize = dataSize;
89 buf = new byte[bufSize];
90 }
91 changeSet.readEntityData(buf, 0, dataSize);
92 if (DEBUG) Log.v(TAG, " data size " + dataSize);
93
94 try {
95 entity.write(buf, 0, dataSize);
96 } catch (IOException e) {
Dan Egnorefe52642009-06-24 00:16:33 -070097 Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
98 return false;
Joe Onorato5d605dc2009-06-18 18:23:43 -070099 } finally {
100 entity.close();
101 }
102 } else {
103 entityFile.delete();
Christopher Tate2fdd4282009-06-12 15:20:04 -0700104 }
105 }
Dan Egnorefe52642009-06-24 00:16:33 -0700106 return true;
Christopher Tate2fdd4282009-06-12 15:20:04 -0700107 } catch (IOException e) {
108 // oops, something went wrong. abort the operation and return error.
Dan Egnorefe52642009-06-24 00:16:33 -0700109 Log.v(TAG, "Exception reading backup input:", e);
110 return false;
Christopher Tate2fdd4282009-06-12 15:20:04 -0700111 }
Dan Egnorefe52642009-06-24 00:16:33 -0700112 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700113
Dan Egnorefe52642009-06-24 00:16:33 -0700114 public boolean finishBackup() throws RemoteException {
115 if (DEBUG) Log.v(TAG, "finishBackup()");
116 return true;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700117 }
118
119 // Restore handling
120 public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
121 // one hardcoded restore set
Christopher Tatef68eb502009-06-16 11:02:01 -0700122 RestoreSet set = new RestoreSet("Local disk image", "flash", 0);
123 RestoreSet[] array = { set };
124 return array;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700125 }
126
Dan Egnorefe52642009-06-24 00:16:33 -0700127 public boolean startRestore(long token, PackageInfo[] packages) {
128 if (DEBUG) Log.v(TAG, "start restore " + token);
129 mRestorePackages = packages;
130 mRestorePackage = -1;
131 return true;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700132 }
133
Dan Egnorefe52642009-06-24 00:16:33 -0700134 public String nextRestorePackage() {
135 if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
136 while (++mRestorePackage < mRestorePackages.length) {
137 String name = mRestorePackages[mRestorePackage].packageName;
138 if (new File(mDataDir, name).isDirectory()) {
139 if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name);
140 return name;
141 }
142 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700143
Dan Egnorefe52642009-06-24 00:16:33 -0700144 if (DEBUG) Log.v(TAG, " no more packages to restore");
145 return "";
146 }
147
148 public boolean getRestoreData(ParcelFileDescriptor outFd) {
149 if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
150 if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
151 File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName);
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700152
Christopher Tate2fdd4282009-06-12 15:20:04 -0700153 // The restore set is the concatenation of the individual record blobs,
154 // each of which is a file in the package's directory
155 File[] blobs = packageDir.listFiles();
Dan Egnorefe52642009-06-24 00:16:33 -0700156 if (blobs == null) {
157 Log.e(TAG, "Error listing directory: " + packageDir);
158 return false; // nextRestorePackage() ensures the dir exists, so this is an error
Christopher Tate2fdd4282009-06-12 15:20:04 -0700159 }
Dan Egnorefe52642009-06-24 00:16:33 -0700160
161 // We expect at least some data if the directory exists in the first place
162 if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.length + " key files");
163 BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
164 try {
165 for (File f : blobs) {
166 FileInputStream in = new FileInputStream(f);
167 try {
168 int size = (int) f.length();
169 byte[] buf = new byte[size];
170 in.read(buf);
171 String key = new String(Base64.decode(f.getName()));
172 if (DEBUG) Log.v(TAG, " ... key=" + key + " size=" + size);
173 out.writeEntityHeader(key, size);
174 out.writeEntityData(buf, size);
175 } finally {
176 in.close();
177 }
178 }
179 return true;
180 } catch (IOException e) {
181 Log.e(TAG, "Unable to read backup records", e);
182 return false;
183 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700184 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700185
Dan Egnorefe52642009-06-24 00:16:33 -0700186 public void finishRestore() {
187 if (DEBUG) Log.v(TAG, "finishRestore()");
Christopher Tate3a31a932009-06-22 15:10:30 -0700188 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700189}