LocalTransport.java revision 0144516e19b9fd5415a56f8b41191187e2344bb0
1package com.android.internal.backup; 2 3import android.backup.BackupDataInput; 4import android.backup.BackupDataOutput; 5import 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 15import org.bouncycastle.util.encoders.Base64; 16 17import 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"; 31 private static final boolean DEBUG = true; 32 33 private static final String TRANSPORT_DIR_NAME 34 = "com.android.internal.backup.LocalTransport"; 35 36 private Context mContext; 37 private PackageManager mPackageManager; 38 private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup"); 39 private PackageInfo[] mRestorePackages = null; 40 private int mRestorePackage = -1; // Index into mRestorePackages 41 42 43 public LocalTransport(Context context) { 44 if (DEBUG) Log.v(TAG, "Transport constructed"); 45 mContext = context; 46 mPackageManager = context.getPackageManager(); 47 } 48 49 50 public String transportDirName() { 51 return TRANSPORT_DIR_NAME; 52 } 53 54 public long requestBackupTime() { 55 // any time is a good time for local backup 56 return 0; 57 } 58 59 public int initializeDevice() { 60 if (DEBUG) Log.v(TAG, "wiping all data"); 61 deleteContents(mDataDir); 62 return BackupConstants.TRANSPORT_OK; 63 } 64 65 public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) { 66 if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName); 67 68 File packageDir = new File(mDataDir, packageInfo.packageName); 69 packageDir.mkdirs(); 70 71 // Each 'record' in the restore set is kept in its own file, named by 72 // the record key. Wind through the data file, extracting individual 73 // record operations and building a set of all the updates to apply 74 // in this update. 75 BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor()); 76 try { 77 int bufSize = 512; 78 byte[] buf = new byte[bufSize]; 79 while (changeSet.readNextHeader()) { 80 String key = changeSet.getKey(); 81 String base64Key = new String(Base64.encode(key.getBytes())); 82 File entityFile = new File(packageDir, base64Key); 83 84 int dataSize = changeSet.getDataSize(); 85 86 if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize 87 + " key64=" + base64Key); 88 89 if (dataSize >= 0) { 90 FileOutputStream entity = new FileOutputStream(entityFile); 91 92 if (dataSize > bufSize) { 93 bufSize = dataSize; 94 buf = new byte[bufSize]; 95 } 96 changeSet.readEntityData(buf, 0, dataSize); 97 if (DEBUG) Log.v(TAG, " data size " + dataSize); 98 99 try { 100 entity.write(buf, 0, dataSize); 101 } catch (IOException e) { 102 Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath()); 103 return BackupConstants.TRANSPORT_ERROR; 104 } finally { 105 entity.close(); 106 } 107 } else { 108 entityFile.delete(); 109 } 110 } 111 return BackupConstants.TRANSPORT_OK; 112 } catch (IOException e) { 113 // oops, something went wrong. abort the operation and return error. 114 Log.v(TAG, "Exception reading backup input:", e); 115 return BackupConstants.TRANSPORT_ERROR; 116 } 117 } 118 119 // Deletes the contents but not the given directory 120 private void deleteContents(File dirname) { 121 File[] contents = dirname.listFiles(); 122 if (contents != null) { 123 for (File f : contents) { 124 if (f.isDirectory()) { 125 // delete the directory's contents then fall through 126 // and delete the directory itself. 127 deleteContents(f); 128 } 129 f.delete(); 130 } 131 } 132 } 133 134 public int clearBackupData(PackageInfo packageInfo) { 135 if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName); 136 137 File packageDir = new File(mDataDir, packageInfo.packageName); 138 for (File f : packageDir.listFiles()) { 139 f.delete(); 140 } 141 packageDir.delete(); 142 return BackupConstants.TRANSPORT_OK; 143 } 144 145 public int finishBackup() { 146 if (DEBUG) Log.v(TAG, "finishBackup()"); 147 return BackupConstants.TRANSPORT_OK; 148 } 149 150 // Restore handling 151 public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { 152 // one hardcoded restore set 153 RestoreSet set = new RestoreSet("Local disk image", "flash", 0); 154 RestoreSet[] array = { set }; 155 return array; 156 } 157 158 public int startRestore(long token, PackageInfo[] packages) { 159 if (DEBUG) Log.v(TAG, "start restore " + token); 160 mRestorePackages = packages; 161 mRestorePackage = -1; 162 return BackupConstants.TRANSPORT_OK; 163 } 164 165 public String nextRestorePackage() { 166 if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); 167 while (++mRestorePackage < mRestorePackages.length) { 168 String name = mRestorePackages[mRestorePackage].packageName; 169 if (new File(mDataDir, name).isDirectory()) { 170 if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name); 171 return name; 172 } 173 } 174 175 if (DEBUG) Log.v(TAG, " no more packages to restore"); 176 return ""; 177 } 178 179 public int getRestoreData(ParcelFileDescriptor outFd) { 180 if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); 181 if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called"); 182 File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName); 183 184 // The restore set is the concatenation of the individual record blobs, 185 // each of which is a file in the package's directory 186 File[] blobs = packageDir.listFiles(); 187 if (blobs == null) { // nextRestorePackage() ensures the dir exists, so this is an error 188 Log.e(TAG, "Error listing directory: " + packageDir); 189 return BackupConstants.TRANSPORT_ERROR; 190 } 191 192 // We expect at least some data if the directory exists in the first place 193 if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.length + " key files"); 194 BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor()); 195 try { 196 for (File f : blobs) { 197 FileInputStream in = new FileInputStream(f); 198 try { 199 int size = (int) f.length(); 200 byte[] buf = new byte[size]; 201 in.read(buf); 202 String key = new String(Base64.decode(f.getName())); 203 if (DEBUG) Log.v(TAG, " ... key=" + key + " size=" + size); 204 out.writeEntityHeader(key, size); 205 out.writeEntityData(buf, size); 206 } finally { 207 in.close(); 208 } 209 } 210 return BackupConstants.TRANSPORT_OK; 211 } catch (IOException e) { 212 Log.e(TAG, "Unable to read backup records", e); 213 return BackupConstants.TRANSPORT_ERROR; 214 } 215 } 216 217 public void finishRestore() { 218 if (DEBUG) Log.v(TAG, "finishRestore()"); 219 } 220} 221