LocalTransport.java revision 3a31a93b8a195ae2d0180e6dfbf292da2e581f50
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"); 369bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate private FileFilter mDirFileFilter = new FileFilter() { 379bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate public boolean accept(File f) { 389bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate return f.isDirectory(); 399bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 409bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate }; 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 499bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate public long requestBackupTime() throws RemoteException { 509bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // any time is a good time for local backup 519bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate return 0; 529bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 539bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 549bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate public int startSession() throws RemoteException { 552fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate if (DEBUG) Log.v(TAG, "session started"); 562fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate mDataDir.mkdirs(); 579bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate return 0; 589bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 599bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 609bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate public int endSession() throws RemoteException { 612fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate if (DEBUG) Log.v(TAG, "session ended"); 629bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate return 0; 639bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 649bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 659bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) 669bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate throws RemoteException { 672fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName); 682fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate int err = 0; 699bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 702fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate File packageDir = new File(mDataDir, packageInfo.packageName); 712fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate packageDir.mkdirs(); 729bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 732fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // Each 'record' in the restore set is kept in its own file, named by 742fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // the record key. Wind through the data file, extracting individual 752fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // record operations and building a set of all the updates to apply 762fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // in this update. 772fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor()); 782fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate try { 792fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate int bufSize = 512; 802fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate byte[] buf = new byte[bufSize]; 812fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate while (changeSet.readNextHeader()) { 822fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate String key = changeSet.getKey(); 835d605dc56b036232e885f6ec36b888b729673060Joe Onorato String base64Key = new String(Base64.encode(key.getBytes())); 845d605dc56b036232e885f6ec36b888b729673060Joe Onorato File entityFile = new File(packageDir, base64Key); 855d605dc56b036232e885f6ec36b888b729673060Joe Onorato 862fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate int dataSize = changeSet.getDataSize(); 87e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate 88e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize 89e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate + " key64=" + base64Key); 902fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate 915d605dc56b036232e885f6ec36b888b729673060Joe Onorato if (dataSize >= 0) { 925d605dc56b036232e885f6ec36b888b729673060Joe Onorato FileOutputStream entity = new FileOutputStream(entityFile); 935d605dc56b036232e885f6ec36b888b729673060Joe Onorato 945d605dc56b036232e885f6ec36b888b729673060Joe Onorato if (dataSize > bufSize) { 955d605dc56b036232e885f6ec36b888b729673060Joe Onorato bufSize = dataSize; 965d605dc56b036232e885f6ec36b888b729673060Joe Onorato buf = new byte[bufSize]; 975d605dc56b036232e885f6ec36b888b729673060Joe Onorato } 985d605dc56b036232e885f6ec36b888b729673060Joe Onorato changeSet.readEntityData(buf, 0, dataSize); 995d605dc56b036232e885f6ec36b888b729673060Joe Onorato if (DEBUG) Log.v(TAG, " data size " + dataSize); 1005d605dc56b036232e885f6ec36b888b729673060Joe Onorato 1015d605dc56b036232e885f6ec36b888b729673060Joe Onorato try { 1025d605dc56b036232e885f6ec36b888b729673060Joe Onorato entity.write(buf, 0, dataSize); 1035d605dc56b036232e885f6ec36b888b729673060Joe Onorato } catch (IOException e) { 1045d605dc56b036232e885f6ec36b888b729673060Joe Onorato Log.e(TAG, "Unable to update key file " 1055d605dc56b036232e885f6ec36b888b729673060Joe Onorato + entityFile.getAbsolutePath()); 1065d605dc56b036232e885f6ec36b888b729673060Joe Onorato err = -1; 1075d605dc56b036232e885f6ec36b888b729673060Joe Onorato } finally { 1085d605dc56b036232e885f6ec36b888b729673060Joe Onorato entity.close(); 1095d605dc56b036232e885f6ec36b888b729673060Joe Onorato } 1105d605dc56b036232e885f6ec36b888b729673060Joe Onorato } else { 1115d605dc56b036232e885f6ec36b888b729673060Joe Onorato entityFile.delete(); 1122fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 1132fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 1142fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } catch (IOException e) { 1152fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // oops, something went wrong. abort the operation and return error. 1162fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate Log.v(TAG, "Exception reading backup input:"); 1172fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate e.printStackTrace(); 1182fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate err = -1; 1192fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 1209bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 1212fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate return err; 1229bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1239bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 1249bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // Restore handling 1259bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { 1269bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // one hardcoded restore set 127f68eb500f99361541049e09eb7f9ddd6f4ef4efaChristopher Tate RestoreSet set = new RestoreSet("Local disk image", "flash", 0); 128f68eb500f99361541049e09eb7f9ddd6f4ef4efaChristopher Tate RestoreSet[] array = { set }; 129f68eb500f99361541049e09eb7f9ddd6f4ef4efaChristopher Tate return array; 1309bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1319bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 1329bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate public PackageInfo[] getAppSet(int token) throws android.os.RemoteException { 1332fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate if (DEBUG) Log.v(TAG, "getting app set " + token); 1349bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // the available packages are the extant subdirs of mDatadir 1359bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate File[] packageDirs = mDataDir.listFiles(mDirFileFilter); 1369bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>(); 1379bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate for (File dir : packageDirs) { 1389bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate try { 1399bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(), 1409bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate PackageManager.GET_SIGNATURES); 1419bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate if (pkg != null) { 1429bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate packages.add(pkg); 1439bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1449bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } catch (NameNotFoundException e) { 1459bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // restore set contains data for a package not installed on the 1469bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // phone -- just ignore it. 1479bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1489bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1499bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 1502fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate if (DEBUG) { 1512fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate Log.v(TAG, "Built app set of " + packages.size() + " entries:"); 1522fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate for (PackageInfo p : packages) { 1532fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate Log.v(TAG, " + " + p.packageName); 1542fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 1559bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1569bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 1579bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate PackageInfo[] result = new PackageInfo[packages.size()]; 1589bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate return packages.toArray(result); 1599bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1609bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 1618e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor outFd) 1629bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate throws android.os.RemoteException { 1632fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate if (DEBUG) Log.v(TAG, "getting restore data " + token + " : " + packageInfo.packageName); 1649bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // we only support one hardcoded restore set 1659bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate if (token != 0) return -1; 1669bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 1679bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate // the data for a given package is at a known location 1689bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate File packageDir = new File(mDataDir, packageInfo.packageName); 1699bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate 1702fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // The restore set is the concatenation of the individual record blobs, 1712fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate // each of which is a file in the package's directory 1722fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate File[] blobs = packageDir.listFiles(); 1733a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate if (DEBUG) Log.v(TAG, " found " + blobs.length + " key files"); 1742fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate int err = 0; 1752fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate if (blobs != null && blobs.length > 0) { 17683248c432ffe2e2a17abbc8e4960c26574b46bcaJoe Onorato BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor()); 1778e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate try { 1788e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate for (File f : blobs) { 1793a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate copyToRestoreData(f, out); 1808e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate } 1818e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate } catch (Exception e) { 1828e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate Log.e(TAG, "Unable to read backup records"); 1838e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate err = -1; 1842fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 1852fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate } 1862fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate return err; 1879bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate } 1883a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate 1893a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate private void copyToRestoreData(File f, BackupDataOutput out) throws IOException { 1903a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate FileInputStream in = new FileInputStream(f); 1913a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate int size = (int) f.length(); 1923a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate byte[] buf = new byte[size]; 1933a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate in.read(buf); 1943a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate String key = new String(Base64.decode(f.getName())); 1953a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate if (DEBUG) Log.v(TAG, " ... copy to stream: key=" + key 1963a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate + " size=" + size); 1973a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate out.writeEntityHeader(key, size); 1983a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate out.writeEntityData(buf, size); 1993a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate } 2009bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate} 201