blob: 5caa0158d134005c4b586c53f0f33192b9e406cb [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();
83 int dataSize = changeSet.getDataSize();
Christopher Tatee9190a22009-06-17 17:52:05 -070084
85 String base64Key = new String(Base64.encode(key.getBytes()));
86 if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize
87 + " key64=" + base64Key);
Christopher Tate2fdd4282009-06-12 15:20:04 -070088 if (dataSize > bufSize) {
89 bufSize = dataSize;
90 buf = new byte[bufSize];
91 }
Joe Onorato5f15d152009-06-16 16:31:35 -040092 changeSet.readEntityData(buf, 0, dataSize);
Christopher Tate2fdd4282009-06-12 15:20:04 -070093 if (DEBUG) Log.v(TAG, " + data size " + dataSize);
Christopher Tate9bbc21a2009-06-10 20:23:25 -070094
Christopher Tatee9190a22009-06-17 17:52:05 -070095 File entityFile = new File(packageDir, base64Key);
Christopher Tate2fdd4282009-06-12 15:20:04 -070096 FileOutputStream entity = new FileOutputStream(entityFile);
97 try {
98 entity.write(buf, 0, dataSize);
99 } catch (IOException e) {
100 Log.e(TAG, "Unable to update key file "
101 + entityFile.getAbsolutePath());
102 err = -1;
103 } finally {
104 entity.close();
105 }
106 }
107 } catch (IOException e) {
108 // oops, something went wrong. abort the operation and return error.
109 Log.v(TAG, "Exception reading backup input:");
110 e.printStackTrace();
111 err = -1;
112 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700113
Christopher Tate2fdd4282009-06-12 15:20:04 -0700114 return err;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700115 }
116
117 // Restore handling
118 public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
119 // one hardcoded restore set
Christopher Tatef68eb502009-06-16 11:02:01 -0700120 RestoreSet set = new RestoreSet("Local disk image", "flash", 0);
121 RestoreSet[] array = { set };
122 return array;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700123 }
124
125 public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -0700126 if (DEBUG) Log.v(TAG, "getting app set " + token);
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700127 // the available packages are the extant subdirs of mDatadir
128 File[] packageDirs = mDataDir.listFiles(mDirFileFilter);
129 ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>();
130 for (File dir : packageDirs) {
131 try {
132 PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(),
133 PackageManager.GET_SIGNATURES);
134 if (pkg != null) {
135 packages.add(pkg);
136 }
137 } catch (NameNotFoundException e) {
138 // restore set contains data for a package not installed on the
139 // phone -- just ignore it.
140 }
141 }
142
Christopher Tate2fdd4282009-06-12 15:20:04 -0700143 if (DEBUG) {
144 Log.v(TAG, "Built app set of " + packages.size() + " entries:");
145 for (PackageInfo p : packages) {
146 Log.v(TAG, " + " + p.packageName);
147 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700148 }
149
150 PackageInfo[] result = new PackageInfo[packages.size()];
151 return packages.toArray(result);
152 }
153
Christopher Tate8e55eac92009-06-15 12:31:20 -0700154 public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor outFd)
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700155 throws android.os.RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -0700156 if (DEBUG) Log.v(TAG, "getting restore data " + token + " : " + packageInfo.packageName);
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700157 // we only support one hardcoded restore set
158 if (token != 0) return -1;
159
160 // the data for a given package is at a known location
161 File packageDir = new File(mDataDir, packageInfo.packageName);
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700162
Christopher Tate2fdd4282009-06-12 15:20:04 -0700163 // The restore set is the concatenation of the individual record blobs,
164 // each of which is a file in the package's directory
165 File[] blobs = packageDir.listFiles();
166 int err = 0;
167 if (blobs != null && blobs.length > 0) {
Joe Onorato83248c42009-06-17 17:55:20 -0700168 BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
Christopher Tate8e55eac92009-06-15 12:31:20 -0700169 try {
170 for (File f : blobs) {
171 FileInputStream in = new FileInputStream(f);
172 int size = (int) f.length();
173 byte[] buf = new byte[size];
174 in.read(buf);
175 out.writeEntityHeader(f.getName(), size);
176 out.writeEntityData(buf, size);
177 }
178 } catch (Exception e) {
179 Log.e(TAG, "Unable to read backup records");
180 err = -1;
Christopher Tate2fdd4282009-06-12 15:20:04 -0700181 }
182 }
Christopher Tate2fdd4282009-06-12 15:20:04 -0700183 return err;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700184 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700185}