LocalTransport.java revision efe52647f6b41993be43a5f47d1178bb0468cec8
19bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tatepackage com.android.internal.backup; 29bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 32fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tateimport android.backup.BackupDataInput; 48e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tateimport android.backup.BackupDataOutput; 59bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.backup.RestoreSet; 69bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.content.Context; 79bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.content.pm.PackageInfo; 89bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.content.pm.PackageManager; 99bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.content.pm.PackageManager.NameNotFoundException; 109bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.os.Environment; 119bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.os.ParcelFileDescriptor; 129bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.os.RemoteException; 139bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.util.Log; 149bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 15e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tateimport org.bouncycastle.util.encoders.Base64; 16e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate 179bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.File; 189bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileFilter; 199bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileInputStream; 209bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileOutputStream; 219bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.IOException; 229bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.util.ArrayList; 239bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 249bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate/** 259bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate * Backup transport for stashing stuff into a known location on disk, and 269bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate * later restoring from there. For testing only. 279bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate */ 289bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 299bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tatepublic class LocalTransport extends IBackupTransport.Stub { 309bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate private static final String TAG = "LocalTransport"; 312fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate private static final boolean DEBUG = true; 329bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 339bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate private Context mContext; 349bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate private PackageManager mPackageManager; 359bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup"); 36efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor private PackageInfo[] mRestorePackages = null; 37efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor private int mRestorePackage = -1; // Index into mRestorePackages 389bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 399bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 409bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate public LocalTransport(Context context) { 412fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate if (DEBUG) Log.v(TAG, "Transport constructed"); 429bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate mContext = context; 439bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate mPackageManager = context.getPackageManager(); 449bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 459bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 469bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate public long requestBackupTime() throws RemoteException { 479bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // any time is a good time for local backup 489bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate return 0; 499bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 509bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 51efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) 529bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate throws RemoteException { 532fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName); 549bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 552fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate File packageDir = new File(mDataDir, packageInfo.packageName); 562fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate packageDir.mkdirs(); 579bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 582fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // Each 'record' in the restore set is kept in its own file, named by 592fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // the record key. Wind through the data file, extracting individual 602fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // record operations and building a set of all the updates to apply 612fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // in this update. 622fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor()); 632fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate try { 642fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate int bufSize = 512; 652fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate byte[] buf = new byte[bufSize]; 662fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate while (changeSet.readNextHeader()) { 672fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate String key = changeSet.getKey(); 685d605dc56b036232e885f6ec36b888b729673060Joe Onorato String base64Key = new String(Base64.encode(key.getBytes())); 695d605dc56b036232e885f6ec36b888b729673060Joe Onorato File entityFile = new File(packageDir, base64Key); 705d605dc56b036232e885f6ec36b888b729673060Joe Onorato 712fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate int dataSize = changeSet.getDataSize(); 72e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate 73e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize 74e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate + " key64=" + base64Key); 752fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 765d605dc56b036232e885f6ec36b888b729673060Joe Onorato if (dataSize >= 0) { 775d605dc56b036232e885f6ec36b888b729673060Joe Onorato FileOutputStream entity = new FileOutputStream(entityFile); 785d605dc56b036232e885f6ec36b888b729673060Joe Onorato 795d605dc56b036232e885f6ec36b888b729673060Joe Onorato if (dataSize > bufSize) { 805d605dc56b036232e885f6ec36b888b729673060Joe Onorato bufSize = dataSize; 815d605dc56b036232e885f6ec36b888b729673060Joe Onorato buf = new byte[bufSize]; 825d605dc56b036232e885f6ec36b888b729673060Joe Onorato } 835d605dc56b036232e885f6ec36b888b729673060Joe Onorato changeSet.readEntityData(buf, 0, dataSize); 845d605dc56b036232e885f6ec36b888b729673060Joe Onorato if (DEBUG) Log.v(TAG, " data size " + dataSize); 855d605dc56b036232e885f6ec36b888b729673060Joe Onorato 865d605dc56b036232e885f6ec36b888b729673060Joe Onorato try { 875d605dc56b036232e885f6ec36b888b729673060Joe Onorato entity.write(buf, 0, dataSize); 885d605dc56b036232e885f6ec36b888b729673060Joe Onorato } catch (IOException e) { 89efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath()); 90efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return false; 915d605dc56b036232e885f6ec36b888b729673060Joe Onorato } finally { 925d605dc56b036232e885f6ec36b888b729673060Joe Onorato entity.close(); 935d605dc56b036232e885f6ec36b888b729673060Joe Onorato } 945d605dc56b036232e885f6ec36b888b729673060Joe Onorato } else { 955d605dc56b036232e885f6ec36b888b729673060Joe Onorato entityFile.delete(); 962fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 972fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 98efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return true; 992fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } catch (IOException e) { 1002fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // oops, something went wrong. abort the operation and return error. 101efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor Log.v(TAG, "Exception reading backup input:", e); 102efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return false; 1032fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 104efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor } 1059bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 106efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor public boolean finishBackup() throws RemoteException { 107efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, "finishBackup()"); 108efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return true; 1099bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1109bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 1119bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // Restore handling 1129bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { 1139bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // one hardcoded restore set 114f68eb500f99361541049e09eb7f9ddd6f4ef4efaChristopher Tate RestoreSet set = new RestoreSet("Local disk image", "flash", 0); 115f68eb500f99361541049e09eb7f9ddd6f4ef4efaChristopher Tate RestoreSet[] array = { set }; 116f68eb500f99361541049e09eb7f9ddd6f4ef4efaChristopher Tate return array; 1179bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1189bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 119efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor public boolean startRestore(long token, PackageInfo[] packages) { 120efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, "start restore " + token); 121efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor mRestorePackages = packages; 122efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor mRestorePackage = -1; 123efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return true; 124efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor } 1259bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 126efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor public String nextRestorePackage() { 127efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); 128efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor while (++mRestorePackage < mRestorePackages.length) { 129efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor String name = mRestorePackages[mRestorePackage].packageName; 130efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (new File(mDataDir, name).isDirectory()) { 131efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name); 132efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return name; 1332fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 1349bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1359bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 136efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, " no more packages to restore"); 137efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return ""; 1389bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1399bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 140efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor public boolean getRestoreData(ParcelFileDescriptor outFd) { 141efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); 142efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called"); 143efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName); 1449bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 1452fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // The restore set is the concatenation of the individual record blobs, 1462fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // each of which is a file in the package's directory 1472fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate File[] blobs = packageDir.listFiles(); 148efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (blobs == null) { 149efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor Log.e(TAG, "Error listing directory: " + packageDir); 150efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return false; // nextRestorePackage() ensures the dir exists, so this is an error 151efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor } 152efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor 153efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor // We expect at least some data if the directory exists in the first place 154efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.length + " key files"); 155efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor()); 156efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor try { 157efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor for (File f : blobs) { 158efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor FileInputStream in = new FileInputStream(f); 159efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor try { 160efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor int size = (int) f.length(); 161efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor byte[] buf = new byte[size]; 162efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor in.read(buf); 163efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor String key = new String(Base64.decode(f.getName())); 164efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, " ... key=" + key + " size=" + size); 165efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor out.writeEntityHeader(key, size); 166efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor out.writeEntityData(buf, size); 167efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor } finally { 168efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor in.close(); 1698e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate } 1702fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 171efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return true; 172efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor } catch (IOException e) { 173efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor Log.e(TAG, "Unable to read backup records", e); 174efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return false; 1752fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 1769bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1773a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate 178efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor public void finishRestore() { 179efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, "finishRestore()"); 1803a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate } 1819bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate} 182