blob: 83182f23b0c6a181fadb1792aabf7ff164e1bc87 [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 Tate9bbc21a2009-06-10 20:23:25 -07004import android.backup.RestoreSet;
5import android.content.Context;
6import android.content.pm.PackageInfo;
7import android.content.pm.PackageManager;
8import android.content.pm.PackageManager.NameNotFoundException;
9import android.os.Environment;
10import android.os.ParcelFileDescriptor;
11import android.os.RemoteException;
12import android.util.Log;
13
14import java.io.File;
15import java.io.FileFilter;
16import java.io.FileInputStream;
17import java.io.FileOutputStream;
18import java.io.IOException;
19import java.util.ArrayList;
20
21/**
22 * Backup transport for stashing stuff into a known location on disk, and
23 * later restoring from there. For testing only.
24 */
25
26public class LocalTransport extends IBackupTransport.Stub {
27 private static final String TAG = "LocalTransport";
Christopher Tate2fdd4282009-06-12 15:20:04 -070028 private static final boolean DEBUG = true;
Christopher Tate9bbc21a2009-06-10 20:23:25 -070029
30 private Context mContext;
31 private PackageManager mPackageManager;
32 private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
33 private FileFilter mDirFileFilter = new FileFilter() {
34 public boolean accept(File f) {
35 return f.isDirectory();
36 }
37 };
38
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
51 public int startSession() throws RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -070052 if (DEBUG) Log.v(TAG, "session started");
53 mDataDir.mkdirs();
Christopher Tate9bbc21a2009-06-10 20:23:25 -070054 return 0;
55 }
56
57 public int endSession() throws RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -070058 if (DEBUG) Log.v(TAG, "session ended");
Christopher Tate9bbc21a2009-06-10 20:23:25 -070059 return 0;
60 }
61
62 public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data)
63 throws RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -070064 if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
65 int err = 0;
66
Christopher Tate9bbc21a2009-06-10 20:23:25 -070067 File packageDir = new File(mDataDir, packageInfo.packageName);
Christopher Tate2fdd4282009-06-12 15:20:04 -070068 packageDir.mkdirs();
Christopher Tate9bbc21a2009-06-10 20:23:25 -070069
Christopher Tate2fdd4282009-06-12 15:20:04 -070070 // Each 'record' in the restore set is kept in its own file, named by
71 // the record key. Wind through the data file, extracting individual
72 // record operations and building a set of all the updates to apply
73 // in this update.
74 BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor());
75 try {
76 int bufSize = 512;
77 byte[] buf = new byte[bufSize];
78 while (changeSet.readNextHeader()) {
79 String key = changeSet.getKey();
80 int dataSize = changeSet.getDataSize();
81 if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize);
82 if (dataSize > bufSize) {
83 bufSize = dataSize;
84 buf = new byte[bufSize];
85 }
86 changeSet.readEntityData(buf, dataSize);
87 if (DEBUG) Log.v(TAG, " + data size " + dataSize);
Christopher Tate9bbc21a2009-06-10 20:23:25 -070088
Christopher Tate2fdd4282009-06-12 15:20:04 -070089 File entityFile = new File(packageDir, key);
90 FileOutputStream entity = new FileOutputStream(entityFile);
91 try {
92 entity.write(buf, 0, dataSize);
93 } catch (IOException e) {
94 Log.e(TAG, "Unable to update key file "
95 + entityFile.getAbsolutePath());
96 err = -1;
97 } finally {
98 entity.close();
99 }
100 }
101 } catch (IOException e) {
102 // oops, something went wrong. abort the operation and return error.
103 Log.v(TAG, "Exception reading backup input:");
104 e.printStackTrace();
105 err = -1;
106 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700107
Christopher Tate2fdd4282009-06-12 15:20:04 -0700108 return err;
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
114 RestoreSet[] set = new RestoreSet[1];
115 set[0].device = "flash";
116 set[0].name = "Local disk image";
117 set[0].token = 0;
118 return set;
119 }
120
121 public PackageInfo[] getAppSet(int token) throws android.os.RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -0700122 if (DEBUG) Log.v(TAG, "getting app set " + token);
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700123 // the available packages are the extant subdirs of mDatadir
124 File[] packageDirs = mDataDir.listFiles(mDirFileFilter);
125 ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>();
126 for (File dir : packageDirs) {
127 try {
128 PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(),
129 PackageManager.GET_SIGNATURES);
130 if (pkg != null) {
131 packages.add(pkg);
132 }
133 } catch (NameNotFoundException e) {
134 // restore set contains data for a package not installed on the
135 // phone -- just ignore it.
136 }
137 }
138
Christopher Tate2fdd4282009-06-12 15:20:04 -0700139 if (DEBUG) {
140 Log.v(TAG, "Built app set of " + packages.size() + " entries:");
141 for (PackageInfo p : packages) {
142 Log.v(TAG, " + " + p.packageName);
143 }
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700144 }
145
146 PackageInfo[] result = new PackageInfo[packages.size()];
147 return packages.toArray(result);
148 }
149
150 public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor output)
151 throws android.os.RemoteException {
Christopher Tate2fdd4282009-06-12 15:20:04 -0700152 if (DEBUG) Log.v(TAG, "getting restore data " + token + " : " + packageInfo.packageName);
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700153 // we only support one hardcoded restore set
154 if (token != 0) return -1;
155
156 // the data for a given package is at a known location
157 File packageDir = new File(mDataDir, packageInfo.packageName);
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700158
Christopher Tate2fdd4282009-06-12 15:20:04 -0700159 // The restore set is the concatenation of the individual record blobs,
160 // each of which is a file in the package's directory
161 File[] blobs = packageDir.listFiles();
162 int err = 0;
163 if (blobs != null && blobs.length > 0) {
164 for (File f : blobs) {
165 err = copyFileToFD(f, output);
166 if (err != 0) break;
167 }
168 }
169
170 return err;
Christopher Tate9bbc21a2009-06-10 20:23:25 -0700171 }
172
173 private int copyFileToFD(File source, ParcelFileDescriptor dest) {
174 try {
175 FileInputStream in = new FileInputStream(source);
176 FileOutputStream out = new FileOutputStream(dest.getFileDescriptor());
177 byte[] buffer = new byte[4096];
178 int bytesRead;
179 while ((bytesRead = in.read(buffer)) >= 0) {
180 out.write(buffer, 0, bytesRead);
181 }
182 } catch (IOException e) {
183 // something went wrong; claim failure
184 return -1;
185 }
186 return 0;
187 }
188}