115a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root/*
215a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * Copyright (C) 2009 The Android Open Source Project
315a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root *
415a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * Licensed under the Apache License, Version 2.0 (the "License");
515a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * you may not use this file except in compliance with the License.
615a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * You may obtain a copy of the License at
715a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root *
815a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root *      http://www.apache.org/licenses/LICENSE-2.0
915a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root *
1015a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * Unless required by applicable law or agreed to in writing, software
1115a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * distributed under the License is distributed on an "AS IS" BASIS,
1215a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1315a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * See the License for the specific language governing permissions and
1415a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root * limitations under the License.
1515a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root */
1615a4d2ffd04dc6c70f2cd17dae12ac6bc14c69abKenny Root
179bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tatepackage com.android.internal.backup;
189bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
194528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tateimport android.app.backup.BackupDataInput;
204528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tateimport android.app.backup.BackupDataOutput;
214528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tateimport android.app.backup.RestoreSet;
229bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.content.Context;
23a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tateimport android.content.Intent;
249bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.content.pm.PackageInfo;
259bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.content.pm.PackageManager;
269bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.content.pm.PackageManager.NameNotFoundException;
279bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.os.Environment;
289bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.os.ParcelFileDescriptor;
299bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.os.RemoteException;
309bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.util.Log;
319bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
324140faeebbfa23d56068c1862b2913fb62145f4fBrian Carlstromimport com.android.org.bouncycastle.util.encoders.Base64;
33e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate
349bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.File;
359bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileFilter;
369bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileInputStream;
379bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileOutputStream;
389bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.IOException;
399bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.util.ArrayList;
409bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
419bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate/**
429bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate * Backup transport for stashing stuff into a known location on disk, and
439bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate * later restoring from there.  For testing only.
449bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate */
459bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
469bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tatepublic class LocalTransport extends IBackupTransport.Stub {
479bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    private static final String TAG = "LocalTransport";
482fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate    private static final boolean DEBUG = true;
499bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
505cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate    private static final String TRANSPORT_DIR_NAME
515cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate            = "com.android.internal.backup.LocalTransport";
525cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
53a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    private static final String TRANSPORT_DESTINATION_STRING
54a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate            = "Backing up to debug-only private cache";
55a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate
5650c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    // The single hardcoded restore set always has the same (nonzero!) token
5750c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    private static final long RESTORE_TOKEN = 1;
5850c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate
599bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    private Context mContext;
609bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
61efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    private PackageInfo[] mRestorePackages = null;
62efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    private int mRestorePackage = -1;  // Index into mRestorePackages
639bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
649bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
659bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    public LocalTransport(Context context) {
669bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        mContext = context;
679bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
689bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
69a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    public Intent configurationIntent() {
70a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        // The local transport is not user-configurable
71a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        return null;
72a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    }
73a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate
74a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    public String currentDestinationString() {
75a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        return TRANSPORT_DESTINATION_STRING;
76a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    }
775cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
780144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public String transportDirName() {
795cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate        return TRANSPORT_DIR_NAME;
805cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate    }
815cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
820144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public long requestBackupTime() {
839bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        // any time is a good time for local backup
849bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        return 0;
859bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
869bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
870144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int initializeDevice() {
880144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        if (DEBUG) Log.v(TAG, "wiping all data");
890144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        deleteContents(mDataDir);
900144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        return BackupConstants.TRANSPORT_OK;
910144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    }
920144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor
930144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
942fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
959bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
962fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        File packageDir = new File(mDataDir, packageInfo.packageName);
972fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        packageDir.mkdirs();
989bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
992fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // Each 'record' in the restore set is kept in its own file, named by
1002fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // the record key.  Wind through the data file, extracting individual
1012fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // record operations and building a set of all the updates to apply
1022fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // in this update.
1032fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor());
1042fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        try {
1052fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            int bufSize = 512;
1062fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            byte[] buf = new byte[bufSize];
1072fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            while (changeSet.readNextHeader()) {
1082fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate                String key = changeSet.getKey();
1095d605dc56b036232e885f6ec36b888b729673060Joe Onorato                String base64Key = new String(Base64.encode(key.getBytes()));
1105d605dc56b036232e885f6ec36b888b729673060Joe Onorato                File entityFile = new File(packageDir, base64Key);
1115d605dc56b036232e885f6ec36b888b729673060Joe Onorato
1122fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate                int dataSize = changeSet.getDataSize();
113e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate
114e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate                if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize
115e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate                        + " key64=" + base64Key);
1162fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate
1175d605dc56b036232e885f6ec36b888b729673060Joe Onorato                if (dataSize >= 0) {
1181afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                    if (entityFile.exists()) {
1191afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                        entityFile.delete();
1201afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                    }
1215d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    FileOutputStream entity = new FileOutputStream(entityFile);
1225d605dc56b036232e885f6ec36b888b729673060Joe Onorato
1235d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    if (dataSize > bufSize) {
1245d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        bufSize = dataSize;
1255d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        buf = new byte[bufSize];
1265d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    }
1275d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    changeSet.readEntityData(buf, 0, dataSize);
1285d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    if (DEBUG) Log.v(TAG, "  data size " + dataSize);
1295d605dc56b036232e885f6ec36b888b729673060Joe Onorato
1305d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    try {
1315d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        entity.write(buf, 0, dataSize);
1325d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    } catch (IOException e) {
133efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                        Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
134d55e18acbe444b74dc9e71eff6ea2c3eaf25fbd0Christopher Tate                        return BackupConstants.TRANSPORT_ERROR;
1355d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    } finally {
1365d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        entity.close();
1375d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    }
1385d605dc56b036232e885f6ec36b888b729673060Joe Onorato                } else {
1395d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    entityFile.delete();
1402fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate                }
1412fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
142d55e18acbe444b74dc9e71eff6ea2c3eaf25fbd0Christopher Tate            return BackupConstants.TRANSPORT_OK;
1432fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        } catch (IOException e) {
1442fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            // oops, something went wrong.  abort the operation and return error.
145efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            Log.v(TAG, "Exception reading backup input:", e);
146d55e18acbe444b74dc9e71eff6ea2c3eaf25fbd0Christopher Tate            return BackupConstants.TRANSPORT_ERROR;
1472fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        }
148efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    }
149ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate
15025a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    // Deletes the contents but not the given directory
15125a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    private void deleteContents(File dirname) {
15225a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        File[] contents = dirname.listFiles();
15325a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        if (contents != null) {
15425a747f5c47f25c1a18961b03507f309b84924feChristopher Tate            for (File f : contents) {
15525a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                if (f.isDirectory()) {
15625a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    // delete the directory's contents then fall through
15725a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    // and delete the directory itself.
15825a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    deleteContents(f);
15925a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                }
16025a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                f.delete();
16125a747f5c47f25c1a18961b03507f309b84924feChristopher Tate            }
16225a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        }
16325a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    }
16425a747f5c47f25c1a18961b03507f309b84924feChristopher Tate
1650144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int clearBackupData(PackageInfo packageInfo) {
166ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate        if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
167ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate
168ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate        File packageDir = new File(mDataDir, packageInfo.packageName);
1690abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate        final File[] fileset = packageDir.listFiles();
1700abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate        if (fileset != null) {
1710abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            for (File f : fileset) {
1720abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate                f.delete();
1730abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            }
1740abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            packageDir.delete();
175ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate        }
1760144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        return BackupConstants.TRANSPORT_OK;
177ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate    }
1789bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1790144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int finishBackup() {
180efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "finishBackup()");
1810144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        return BackupConstants.TRANSPORT_OK;
1829bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
1839bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1849bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    // Restore handling
1859bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
1869bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        // one hardcoded restore set
18750c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate        RestoreSet set = new RestoreSet("Local disk image", "flash", RESTORE_TOKEN);
188f68eb500f99361541049e09eb7f9ddd6f4ef4efaChristopher Tate        RestoreSet[] array = { set };
189f68eb500f99361541049e09eb7f9ddd6f4ef4efaChristopher Tate        return array;
1909bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
1919bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
19250c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    public long getCurrentRestoreSet() {
19350c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate        // The hardcoded restore set always has the same token
19450c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate        return RESTORE_TOKEN;
19550c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    }
19650c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate
1970144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int startRestore(long token, PackageInfo[] packages) {
198efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "start restore " + token);
199efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        mRestorePackages = packages;
200efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        mRestorePackage = -1;
2010144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        return BackupConstants.TRANSPORT_OK;
202efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    }
2039bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
204efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    public String nextRestorePackage() {
205efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
206efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        while (++mRestorePackage < mRestorePackages.length) {
207efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            String name = mRestorePackages[mRestorePackage].packageName;
208efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            if (new File(mDataDir, name).isDirectory()) {
209efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                if (DEBUG) Log.v(TAG, "  nextRestorePackage() = " + name);
210efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                return name;
2112fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
2129bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        }
2139bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
214efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "  no more packages to restore");
215efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        return "";
2169bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
2179bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
2180144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int getRestoreData(ParcelFileDescriptor outFd) {
219efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
220efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
221efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName);
2229bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
2232fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // The restore set is the concatenation of the individual record blobs,
2242fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // each of which is a file in the package's directory
2252fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        File[] blobs = packageDir.listFiles();
2260144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        if (blobs == null) {  // nextRestorePackage() ensures the dir exists, so this is an error
227efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            Log.e(TAG, "Error listing directory: " + packageDir);
2280144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor            return BackupConstants.TRANSPORT_ERROR;
229efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        }
230efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor
231efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        // We expect at least some data if the directory exists in the first place
232efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "  getRestoreData() found " + blobs.length + " key files");
233efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
234efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        try {
235efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            for (File f : blobs) {
236efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                FileInputStream in = new FileInputStream(f);
237efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                try {
238efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    int size = (int) f.length();
239efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    byte[] buf = new byte[size];
240efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    in.read(buf);
241efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    String key = new String(Base64.decode(f.getName()));
242efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    if (DEBUG) Log.v(TAG, "    ... key=" + key + " size=" + size);
243efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    out.writeEntityHeader(key, size);
244efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    out.writeEntityData(buf, size);
245efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                } finally {
246efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    in.close();
2478e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate                }
2482fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
2490144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor            return BackupConstants.TRANSPORT_OK;
250efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        } catch (IOException e) {
251efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            Log.e(TAG, "Unable to read backup records", e);
2520144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor            return BackupConstants.TRANSPORT_ERROR;
2532fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        }
2549bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
2553a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate
256efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    public void finishRestore() {
257efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "finishRestore()");
2583a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate    }
2599bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate}
260