LocalTransport.java revision 25a747f5c47f25c1a18961b03507f309b84924fe
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 335cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate private static final String TRANSPORT_DIR_NAME 345cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate = "com.android.internal.backup.LocalTransport"; 355cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate 369bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate private Context mContext; 379bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate private PackageManager mPackageManager; 389bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup"); 39efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor private PackageInfo[] mRestorePackages = null; 40efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor private int mRestorePackage = -1; // Index into mRestorePackages 419bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 429bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 439bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate public LocalTransport(Context context) { 442fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate if (DEBUG) Log.v(TAG, "Transport constructed"); 459bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate mContext = context; 469bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate mPackageManager = context.getPackageManager(); 479bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 489bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 495cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate 505cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate public String transportDirName() throws RemoteException { 515cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate return TRANSPORT_DIR_NAME; 525cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate } 535cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate 549bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate public long requestBackupTime() throws RemoteException { 559bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // any time is a good time for local backup 569bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate return 0; 579bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 589bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 5925a747f5c47f25c1a18961b03507f309b84924feChristopher Tate public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data, 6025a747f5c47f25c1a18961b03507f309b84924feChristopher Tate boolean wipeAllFirst) throws RemoteException { 612fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName); 629bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 632fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate File packageDir = new File(mDataDir, packageInfo.packageName); 642fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate packageDir.mkdirs(); 6525a747f5c47f25c1a18961b03507f309b84924feChristopher Tate if (wipeAllFirst) { 6625a747f5c47f25c1a18961b03507f309b84924feChristopher Tate if (DEBUG) Log.v(TAG, "wiping all data first"); 6725a747f5c47f25c1a18961b03507f309b84924feChristopher Tate deleteContents(mDataDir); 6825a747f5c47f25c1a18961b03507f309b84924feChristopher Tate } 699bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 702fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // Each 'record' in the restore set is kept in its own file, named by 712fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // the record key. Wind through the data file, extracting individual 722fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // record operations and building a set of all the updates to apply 732fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // in this update. 742fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor()); 752fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate try { 762fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate int bufSize = 512; 772fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate byte[] buf = new byte[bufSize]; 782fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate while (changeSet.readNextHeader()) { 792fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate String key = changeSet.getKey(); 805d605dc56b036232e885f6ec36b888b729673060Joe Onorato String base64Key = new String(Base64.encode(key.getBytes())); 815d605dc56b036232e885f6ec36b888b729673060Joe Onorato File entityFile = new File(packageDir, base64Key); 825d605dc56b036232e885f6ec36b888b729673060Joe Onorato 832fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate int dataSize = changeSet.getDataSize(); 84e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate 85e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize 86e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate + " key64=" + base64Key); 872fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 885d605dc56b036232e885f6ec36b888b729673060Joe Onorato if (dataSize >= 0) { 895d605dc56b036232e885f6ec36b888b729673060Joe Onorato FileOutputStream entity = new FileOutputStream(entityFile); 905d605dc56b036232e885f6ec36b888b729673060Joe Onorato 915d605dc56b036232e885f6ec36b888b729673060Joe Onorato if (dataSize > bufSize) { 925d605dc56b036232e885f6ec36b888b729673060Joe Onorato bufSize = dataSize; 935d605dc56b036232e885f6ec36b888b729673060Joe Onorato buf = new byte[bufSize]; 945d605dc56b036232e885f6ec36b888b729673060Joe Onorato } 955d605dc56b036232e885f6ec36b888b729673060Joe Onorato changeSet.readEntityData(buf, 0, dataSize); 965d605dc56b036232e885f6ec36b888b729673060Joe Onorato if (DEBUG) Log.v(TAG, " data size " + dataSize); 975d605dc56b036232e885f6ec36b888b729673060Joe Onorato 985d605dc56b036232e885f6ec36b888b729673060Joe Onorato try { 995d605dc56b036232e885f6ec36b888b729673060Joe Onorato entity.write(buf, 0, dataSize); 1005d605dc56b036232e885f6ec36b888b729673060Joe Onorato } catch (IOException e) { 101efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath()); 102efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return false; 1035d605dc56b036232e885f6ec36b888b729673060Joe Onorato } finally { 1045d605dc56b036232e885f6ec36b888b729673060Joe Onorato entity.close(); 1055d605dc56b036232e885f6ec36b888b729673060Joe Onorato } 1065d605dc56b036232e885f6ec36b888b729673060Joe Onorato } else { 1075d605dc56b036232e885f6ec36b888b729673060Joe Onorato entityFile.delete(); 1082fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 1092fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 110efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return true; 1112fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } catch (IOException e) { 1122fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // oops, something went wrong. abort the operation and return error. 113efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor Log.v(TAG, "Exception reading backup input:", e); 114efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return false; 1152fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 116efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor } 117ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate 11825a747f5c47f25c1a18961b03507f309b84924feChristopher Tate // Deletes the contents but not the given directory 11925a747f5c47f25c1a18961b03507f309b84924feChristopher Tate private void deleteContents(File dirname) { 12025a747f5c47f25c1a18961b03507f309b84924feChristopher Tate File[] contents = dirname.listFiles(); 12125a747f5c47f25c1a18961b03507f309b84924feChristopher Tate if (contents != null) { 12225a747f5c47f25c1a18961b03507f309b84924feChristopher Tate for (File f : contents) { 12325a747f5c47f25c1a18961b03507f309b84924feChristopher Tate if (f.isDirectory()) { 12425a747f5c47f25c1a18961b03507f309b84924feChristopher Tate // delete the directory's contents then fall through 12525a747f5c47f25c1a18961b03507f309b84924feChristopher Tate // and delete the directory itself. 12625a747f5c47f25c1a18961b03507f309b84924feChristopher Tate deleteContents(f); 12725a747f5c47f25c1a18961b03507f309b84924feChristopher Tate } 12825a747f5c47f25c1a18961b03507f309b84924feChristopher Tate f.delete(); 12925a747f5c47f25c1a18961b03507f309b84924feChristopher Tate } 13025a747f5c47f25c1a18961b03507f309b84924feChristopher Tate } 13125a747f5c47f25c1a18961b03507f309b84924feChristopher Tate } 13225a747f5c47f25c1a18961b03507f309b84924feChristopher Tate 133ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate public boolean clearBackupData(PackageInfo packageInfo) { 134ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName); 135ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate 136ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate File packageDir = new File(mDataDir, packageInfo.packageName); 137ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate for (File f : packageDir.listFiles()) { 138ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate f.delete(); 139ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate } 140ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate packageDir.delete(); 141ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate return true; 142ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate } 1439bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 144efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor public boolean finishBackup() throws RemoteException { 145efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, "finishBackup()"); 146efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return true; 1479bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1489bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 1499bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // Restore handling 1509bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { 1519bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // one hardcoded restore set 152f68eb500f99361541049e09eb7f9ddd6f4ef4efaChristopher Tate RestoreSet set = new RestoreSet("Local disk image", "flash", 0); 153f68eb500f99361541049e09eb7f9ddd6f4ef4efaChristopher Tate RestoreSet[] array = { set }; 154f68eb500f99361541049e09eb7f9ddd6f4ef4efaChristopher Tate return array; 1559bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1569bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 157efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor public boolean startRestore(long token, PackageInfo[] packages) { 158efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, "start restore " + token); 159efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor mRestorePackages = packages; 160efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor mRestorePackage = -1; 161efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return true; 162efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor } 1639bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 164efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor public String nextRestorePackage() { 165efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); 166efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor while (++mRestorePackage < mRestorePackages.length) { 167efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor String name = mRestorePackages[mRestorePackage].packageName; 168efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (new File(mDataDir, name).isDirectory()) { 169efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name); 170efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return name; 1712fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 1729bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1739bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 174efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, " no more packages to restore"); 175efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return ""; 1769bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1779bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 178efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor public boolean getRestoreData(ParcelFileDescriptor outFd) { 179efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); 180efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called"); 181efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName); 1829bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 1832fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // The restore set is the concatenation of the individual record blobs, 1842fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // each of which is a file in the package's directory 1852fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate File[] blobs = packageDir.listFiles(); 186efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (blobs == null) { 187efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor Log.e(TAG, "Error listing directory: " + packageDir); 188efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return false; // nextRestorePackage() ensures the dir exists, so this is an error 189efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor } 190efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor 191efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor // We expect at least some data if the directory exists in the first place 192efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, " getRestoreData() found " + blobs.length + " key files"); 193efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor()); 194efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor try { 195efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor for (File f : blobs) { 196efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor FileInputStream in = new FileInputStream(f); 197efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor try { 198efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor int size = (int) f.length(); 199efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor byte[] buf = new byte[size]; 200efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor in.read(buf); 201efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor String key = new String(Base64.decode(f.getName())); 202efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, " ... key=" + key + " size=" + size); 203efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor out.writeEntityHeader(key, size); 204efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor out.writeEntityData(buf, size); 205efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor } finally { 206efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor in.close(); 2078e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate } 2082fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 209efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return true; 210efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor } catch (IOException e) { 211efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor Log.e(TAG, "Unable to read backup records", e); 212efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor return false; 2132fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 2149bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 2153a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate 216efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor public void finishRestore() { 217efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor if (DEBUG) Log.v(TAG, "finishRestore()"); 2183a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate } 2199bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate} 220