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