blob: 3dd71f3154254ba00fb6299c4aa837931906f064 [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");
36 private FileFilter mDirFileFilter = new FileFilter() {
37 public boolean accept(File f) {
38 return f.isDirectory();
39 }
40 };
41
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
49 public long requestBackupTime() throws RemoteException {
50 // any time is a good time for local backup
51 return 0;
52 }
53
54 public int startSession() throws RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -070055 if (DEBUG) Log.v(TAG, "session started");
56 mDataDir.mkdirs();
Christopher Tate9bbc21a2009-06-10 20:23:25 -070057 return 0;
58 }
59
60 public int endSession() throws RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -070061 if (DEBUG) Log.v(TAG, "session ended");
Christopher Tate9bbc21a2009-06-10 20:23:25 -070062 return 0;
63 }
64
65 public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
66 throws RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -070067 if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
68 int err = 0;
69
Christopher Tate9bbc21a2009-06-10 20:23:25 -070070 File packageDir = new File(mDataDir, packageInfo.packageName);
Christopher Tate2fdd4282009-06-12 15:20:04 -070071 packageDir.mkdirs();
Christopher Tate9bbc21a2009-06-10 20:23:25 -070072
Christopher Tate2fdd4282009-06-12 15:20:04 -070073 // Each 'record' in the restore set is kept in its own file, named by
74 // the record key. Wind through the data file, extracting individual
75 // record operations and building a set of all the updates to apply
76 // in this update.
77 BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor());
78 try {
79 int bufSize = 512;
80 byte[] buf = new byte[bufSize];
81 while (changeSet.readNextHeader()) {
82 String key = changeSet.getKey();
Joe Onorato5d605dc2009-06-18 18:23:43 -070083 String base64Key = new String(Base64.encode(key.getBytes()));
84 File entityFile = new File(packageDir, base64Key);
85
Christopher Tate2fdd4282009-06-12 15:20:04 -070086 int dataSize = changeSet.getDataSize();
Christopher Tatee9190a22009-06-17 17:52:05 -070087
Christopher Tatee9190a22009-06-17 17:52:05 -070088 if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize
89 + " key64=" + base64Key);
Christopher Tate9bbc21a2009-06-10 20:23:25 -070090
Joe Onorato5d605dc2009-06-18 18:23:43 -070091 if (dataSize >= 0) {
92 FileOutputStream entity = new FileOutputStream(entityFile);
93
94 if (dataSize > bufSize) {
95 bufSize = dataSize;
96 buf = new byte[bufSize];
97 }
98 changeSet.readEntityData(buf, 0, dataSize);
99 if (DEBUG) Log.v(TAG, " data size " + dataSize);
100
101 try {
102 entity.write(buf, 0, dataSize);
103 } catch (IOException e) {
104 Log.e(TAG, "Unable to update key file "
105 + entityFile.getAbsolutePath());
106 err = -1;
107 } finally {
108 entity.close();
109 }
110 } else {
111 entityFile.delete();
Christopher Tate2fdd4282009-06-12 15:20:04 -0700112 }
113 }
114 } catch (IOException e) {
115 // oops, something went wrong. abort the operation and return error.
116 Log.v(TAG, "Exception reading backup input:");
117 e.printStackTrace();
118 err = -1;
119 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700120
Christopher Tate2fdd4282009-06-12 15:20:04 -0700121 return err;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700122 }
123
124 // Restore handling
125 public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
126 // one hardcoded restore set
Christopher Tatef68eb502009-06-16 11:02:01 -0700127 RestoreSet set = new RestoreSet("Local disk image", "flash", 0);
128 RestoreSet[] array = { set };
129 return array;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700130 }
131
132 public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -0700133 if (DEBUG) Log.v(TAG, "getting app set " + token);
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700134 // the available packages are the extant subdirs of mDatadir
135 File[] packageDirs = mDataDir.listFiles(mDirFileFilter);
136 ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>();
137 for (File dir : packageDirs) {
138 try {
139 PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(),
140 PackageManager.GET_SIGNATURES);
141 if (pkg != null) {
142 packages.add(pkg);
143 }
144 } catch (NameNotFoundException e) {
145 // restore set contains data for a package not installed on the
146 // phone -- just ignore it.
147 }
148 }
149
Christopher Tate2fdd4282009-06-12 15:20:04 -0700150 if (DEBUG) {
151 Log.v(TAG, "Built app set of " + packages.size() + " entries:");
152 for (PackageInfo p : packages) {
153 Log.v(TAG, " + " + p.packageName);
154 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700155 }
156
157 PackageInfo[] result = new PackageInfo[packages.size()];
158 return packages.toArray(result);
159 }
160
Christopher Tate8e55eac92009-06-15 12:31:20 -0700161 public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor outFd)
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700162 throws android.os.RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -0700163 if (DEBUG) Log.v(TAG, "getting restore data " + token + " : " + packageInfo.packageName);
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700164 // we only support one hardcoded restore set
165 if (token != 0) return -1;
166
167 // the data for a given package is at a known location
168 File packageDir = new File(mDataDir, packageInfo.packageName);
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700169
Christopher Tate2fdd4282009-06-12 15:20:04 -0700170 // The restore set is the concatenation of the individual record blobs,
171 // each of which is a file in the package's directory
172 File[] blobs = packageDir.listFiles();
Christopher Tate3a31a932009-06-22 15:10:30 -0700173 if (DEBUG) Log.v(TAG, " found " + blobs.length + " key files");
Christopher Tate2fdd4282009-06-12 15:20:04 -0700174 int err = 0;
175 if (blobs != null && blobs.length > 0) {
Joe Onorato83248c42009-06-17 17:55:20 -0700176 BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
Christopher Tate8e55eac92009-06-15 12:31:20 -0700177 try {
178 for (File f : blobs) {
Christopher Tate3a31a932009-06-22 15:10:30 -0700179 copyToRestoreData(f, out);
Christopher Tate8e55eac92009-06-15 12:31:20 -0700180 }
181 } catch (Exception e) {
182 Log.e(TAG, "Unable to read backup records");
183 err = -1;
Christopher Tate2fdd4282009-06-12 15:20:04 -0700184 }
185 }
Christopher Tate2fdd4282009-06-12 15:20:04 -0700186 return err;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700187 }
Christopher Tate3a31a932009-06-22 15:10:30 -0700188
189 private void copyToRestoreData(File f, BackupDataOutput out) throws IOException {
190 FileInputStream in = new FileInputStream(f);
191 int size = (int) f.length();
192 byte[] buf = new byte[size];
193 in.read(buf);
194 String key = new String(Base64.decode(f.getName()));
195 if (DEBUG) Log.v(TAG, " ... copy to stream: key=" + key
196 + " size=" + size);
197 out.writeEntityHeader(key, size);
198 out.writeEntityData(buf, size);
199 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700200}