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