LocalTransport.java revision ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66
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 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 66 // Each 'record' in the restore set is kept in its own file, named by 67 // the record key. Wind through the data file, extracting individual 68 // record operations and building a set of all the updates to apply 69 // in this update. 70 BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor()); 71 try { 72 int bufSize = 512; 73 byte[] buf = new byte[bufSize]; 74 while (changeSet.readNextHeader()) { 75 String key = changeSet.getKey(); 76 String base64Key = new String(Base64.encode(key.getBytes())); 77 File entityFile = new File(packageDir, base64Key); 78 79 int dataSize = changeSet.getDataSize(); 80 81 if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize 82 + " key64=" + base64Key); 83 84 if (dataSize >= 0) { 85 FileOutputStream entity = new FileOutputStream(entityFile); 86 87 if (dataSize > bufSize) { 88 bufSize = dataSize; 89 buf = new byte[bufSize]; 90 } 91 changeSet.readEntityData(buf, 0, dataSize); 92 if (DEBUG) Log.v(TAG, " data size " + dataSize); 93 94 try { 95 entity.write(buf, 0, dataSize); 96 } catch (IOException e) { 97 Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath()); 98 return false; 99 } finally { 100 entity.close(); 101 } 102 } else { 103 entityFile.delete(); 104 } 105 } 106 return true; 107 } catch (IOException e) { 108 // oops, something went wrong. abort the operation and return error. 109 Log.v(TAG, "Exception reading backup input:", e); 110 return false; 111 } 112 } 113 114 public boolean clearBackupData(PackageInfo packageInfo) { 115 if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName); 116 117 File packageDir = new File(mDataDir, packageInfo.packageName); 118 for (File f : packageDir.listFiles()) { 119 f.delete(); 120 } 121 packageDir.delete(); 122 return true; 123 } 124 125 public boolean finishBackup() throws RemoteException { 126 if (DEBUG) Log.v(TAG, "finishBackup()"); 127 return true; 128 } 129 130 // Restore handling 131 public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { 132 // one hardcoded restore set 133 RestoreSet set = new RestoreSet("Local disk image", "flash", 0); 134 RestoreSet[] array = { set }; 135 return array; 136 } 137 138 public boolean startRestore(long token, PackageInfo[] packages) { 139 if (DEBUG) Log.v(TAG, "start restore " + token); 140 mRestorePackages = packages; 141 mRestorePackage = -1; 142 return true; 143 } 144 145 public String nextRestorePackage() { 146 if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); 147 while (++mRestorePackage < mRestorePackages.length) { 148 String name = mRestorePackages[mRestorePackage].packageName; 149 if (new File(mDataDir, name).isDirectory()) { 150 if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name); 151 return name; 152 } 153 } 154 155 if (DEBUG) Log.v(TAG, " no more packages to restore"); 156 return ""; 157 } 158 159 public boolean getRestoreData(ParcelFileDescriptor outFd) { 160 if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); 161 if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called"); 162 File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName); 163 164 // The restore set is the concatenation of the individual record blobs, 165 // each of which is a file in the package's directory 166 File[] blobs = packageDir.listFiles(); 167 if (blobs == null) { 168 Log.e(TAG, "Error listing directory: " + packageDir); 169 return false; // nextRestorePackage() ensures the dir exists, so this is an error 170 } 171 172 // We expect at least some data if the directory exists in the first place 173 if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.length + " key files"); 174 BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor()); 175 try { 176 for (File f : blobs) { 177 FileInputStream in = new FileInputStream(f); 178 try { 179 int size = (int) f.length(); 180 byte[] buf = new byte[size]; 181 in.read(buf); 182 String key = new String(Base64.decode(f.getName())); 183 if (DEBUG) Log.v(TAG, " ... key=" + key + " size=" + size); 184 out.writeEntityHeader(key, size); 185 out.writeEntityData(buf, size); 186 } finally { 187 in.close(); 188 } 189 } 190 return true; 191 } catch (IOException e) { 192 Log.e(TAG, "Unable to read backup records", e); 193 return false; 194 } 195 } 196 197 public void finishRestore() { 198 if (DEBUG) Log.v(TAG, "finishRestore()"); 199 } 200} 201