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;
30ebab0ae105f1a6df593a4bc2549fae3ee8b2ade4rpcraigimport android.os.SELinux;
319bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.util.Log;
329bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
334140faeebbfa23d56068c1862b2913fb62145f4fBrian Carlstromimport com.android.org.bouncycastle.util.encoders.Base64;
34e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate
359bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.File;
369bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileFilter;
379bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileInputStream;
389bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileOutputStream;
399bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.IOException;
409bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.util.ArrayList;
419bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
429bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate/**
439bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate * Backup transport for stashing stuff into a known location on disk, and
449bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate * later restoring from there.  For testing only.
459bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate */
469bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
479bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tatepublic class LocalTransport extends IBackupTransport.Stub {
489bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    private static final String TAG = "LocalTransport";
492fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate    private static final boolean DEBUG = true;
509bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
515cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate    private static final String TRANSPORT_DIR_NAME
525cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate            = "com.android.internal.backup.LocalTransport";
535cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
54a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    private static final String TRANSPORT_DESTINATION_STRING
55a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate            = "Backing up to debug-only private cache";
56a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate
5750c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    // The single hardcoded restore set always has the same (nonzero!) token
5850c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    private static final long RESTORE_TOKEN = 1;
5950c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate
609bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    private Context mContext;
619bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
62efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    private PackageInfo[] mRestorePackages = null;
63efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    private int mRestorePackage = -1;  // Index into mRestorePackages
649bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
659bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
669bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    public LocalTransport(Context context) {
679bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        mContext = context;
68ebab0ae105f1a6df593a4bc2549fae3ee8b2ade4rpcraig        mDataDir.mkdirs();
69ebab0ae105f1a6df593a4bc2549fae3ee8b2ade4rpcraig        if (!SELinux.restorecon(mDataDir)) {
70ebab0ae105f1a6df593a4bc2549fae3ee8b2ade4rpcraig            Log.e(TAG, "SELinux restorecon failed for " + mDataDir);
71ebab0ae105f1a6df593a4bc2549fae3ee8b2ade4rpcraig        }
729bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
739bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
74a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    public Intent configurationIntent() {
75a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        // The local transport is not user-configurable
76a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        return null;
77a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    }
78a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate
79a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    public String currentDestinationString() {
80a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        return TRANSPORT_DESTINATION_STRING;
81a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    }
825cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
830144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public String transportDirName() {
845cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate        return TRANSPORT_DIR_NAME;
855cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate    }
865cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
870144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public long requestBackupTime() {
889bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        // any time is a good time for local backup
899bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        return 0;
909bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
919bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
920144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int initializeDevice() {
930144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        if (DEBUG) Log.v(TAG, "wiping all data");
940144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        deleteContents(mDataDir);
950144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        return BackupConstants.TRANSPORT_OK;
960144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    }
970144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor
980144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
992fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
1009bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1012fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        File packageDir = new File(mDataDir, packageInfo.packageName);
1022fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        packageDir.mkdirs();
1039bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1042fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // Each 'record' in the restore set is kept in its own file, named by
1052fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // the record key.  Wind through the data file, extracting individual
1062fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // record operations and building a set of all the updates to apply
1072fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // in this update.
1082fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor());
1092fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        try {
1102fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            int bufSize = 512;
1112fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            byte[] buf = new byte[bufSize];
1122fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            while (changeSet.readNextHeader()) {
1132fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate                String key = changeSet.getKey();
1145d605dc56b036232e885f6ec36b888b729673060Joe Onorato                String base64Key = new String(Base64.encode(key.getBytes()));
1155d605dc56b036232e885f6ec36b888b729673060Joe Onorato                File entityFile = new File(packageDir, base64Key);
1165d605dc56b036232e885f6ec36b888b729673060Joe Onorato
1172fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate                int dataSize = changeSet.getDataSize();
118e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate
119e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate                if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize
120e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate                        + " key64=" + base64Key);
1212fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate
1225d605dc56b036232e885f6ec36b888b729673060Joe Onorato                if (dataSize >= 0) {
1231afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                    if (entityFile.exists()) {
1241afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                        entityFile.delete();
1251afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                    }
1265d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    FileOutputStream entity = new FileOutputStream(entityFile);
1275d605dc56b036232e885f6ec36b888b729673060Joe Onorato
1285d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    if (dataSize > bufSize) {
1295d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        bufSize = dataSize;
1305d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        buf = new byte[bufSize];
1315d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    }
1325d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    changeSet.readEntityData(buf, 0, dataSize);
1335d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    if (DEBUG) Log.v(TAG, "  data size " + dataSize);
1345d605dc56b036232e885f6ec36b888b729673060Joe Onorato
1355d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    try {
1365d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        entity.write(buf, 0, dataSize);
1375d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    } catch (IOException e) {
138efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                        Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
139d55e18acbe444b74dc9e71eff6ea2c3eaf25fbd0Christopher Tate                        return BackupConstants.TRANSPORT_ERROR;
1405d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    } finally {
1415d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        entity.close();
1425d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    }
1435d605dc56b036232e885f6ec36b888b729673060Joe Onorato                } else {
1445d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    entityFile.delete();
1452fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate                }
1462fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
147d55e18acbe444b74dc9e71eff6ea2c3eaf25fbd0Christopher Tate            return BackupConstants.TRANSPORT_OK;
1482fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        } catch (IOException e) {
1492fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            // oops, something went wrong.  abort the operation and return error.
150efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            Log.v(TAG, "Exception reading backup input:", e);
151d55e18acbe444b74dc9e71eff6ea2c3eaf25fbd0Christopher Tate            return BackupConstants.TRANSPORT_ERROR;
1522fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        }
153efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    }
154ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate
15525a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    // Deletes the contents but not the given directory
15625a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    private void deleteContents(File dirname) {
15725a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        File[] contents = dirname.listFiles();
15825a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        if (contents != null) {
15925a747f5c47f25c1a18961b03507f309b84924feChristopher Tate            for (File f : contents) {
16025a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                if (f.isDirectory()) {
16125a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    // delete the directory's contents then fall through
16225a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    // and delete the directory itself.
16325a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    deleteContents(f);
16425a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                }
16525a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                f.delete();
16625a747f5c47f25c1a18961b03507f309b84924feChristopher Tate            }
16725a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        }
16825a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    }
16925a747f5c47f25c1a18961b03507f309b84924feChristopher Tate
1700144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int clearBackupData(PackageInfo packageInfo) {
171ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate        if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
172ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate
173ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate        File packageDir = new File(mDataDir, packageInfo.packageName);
1740abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate        final File[] fileset = packageDir.listFiles();
1750abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate        if (fileset != null) {
1760abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            for (File f : fileset) {
1770abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate                f.delete();
1780abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            }
1790abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            packageDir.delete();
180ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate        }
1810144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        return BackupConstants.TRANSPORT_OK;
182ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate    }
1839bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1840144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int finishBackup() {
185efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "finishBackup()");
1860144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        return BackupConstants.TRANSPORT_OK;
1879bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
1889bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1899bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    // Restore handling
1909bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
1919bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        // one hardcoded restore set
19250c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate        RestoreSet set = new RestoreSet("Local disk image", "flash", RESTORE_TOKEN);
193f68eb500f99361541049e09eb7f9ddd6f4ef4efaChristopher Tate        RestoreSet[] array = { set };
194f68eb500f99361541049e09eb7f9ddd6f4ef4efaChristopher Tate        return array;
1959bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
1969bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
19750c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    public long getCurrentRestoreSet() {
19850c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate        // The hardcoded restore set always has the same token
19950c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate        return RESTORE_TOKEN;
20050c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    }
20150c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate
2020144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int startRestore(long token, PackageInfo[] packages) {
203efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "start restore " + token);
204efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        mRestorePackages = packages;
205efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        mRestorePackage = -1;
2060144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        return BackupConstants.TRANSPORT_OK;
207efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    }
2089bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
209efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    public String nextRestorePackage() {
210efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
211efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        while (++mRestorePackage < mRestorePackages.length) {
212efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            String name = mRestorePackages[mRestorePackage].packageName;
213efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            if (new File(mDataDir, name).isDirectory()) {
214efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                if (DEBUG) Log.v(TAG, "  nextRestorePackage() = " + name);
215efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                return name;
2162fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
2179bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        }
2189bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
219efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "  no more packages to restore");
220efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        return "";
2219bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
2229bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
2230144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int getRestoreData(ParcelFileDescriptor outFd) {
224efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
225efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
226efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName);
2279bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
2282fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // The restore set is the concatenation of the individual record blobs,
2292fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // each of which is a file in the package's directory
2302fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        File[] blobs = packageDir.listFiles();
2310144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        if (blobs == null) {  // nextRestorePackage() ensures the dir exists, so this is an error
232efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            Log.e(TAG, "Error listing directory: " + packageDir);
2330144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor            return BackupConstants.TRANSPORT_ERROR;
234efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        }
235efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor
236efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        // We expect at least some data if the directory exists in the first place
237efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "  getRestoreData() found " + blobs.length + " key files");
238efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
239efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        try {
240efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            for (File f : blobs) {
241efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                FileInputStream in = new FileInputStream(f);
242efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                try {
243efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    int size = (int) f.length();
244efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    byte[] buf = new byte[size];
245efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    in.read(buf);
246efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    String key = new String(Base64.decode(f.getName()));
247efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    if (DEBUG) Log.v(TAG, "    ... key=" + key + " size=" + size);
248efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    out.writeEntityHeader(key, size);
249efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    out.writeEntityData(buf, size);
250efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                } finally {
251efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    in.close();
2528e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate                }
2532fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
2540144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor            return BackupConstants.TRANSPORT_OK;
255efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        } catch (IOException e) {
256efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            Log.e(TAG, "Unable to read backup records", e);
2570144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor            return BackupConstants.TRANSPORT_ERROR;
2582fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        }
2599bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
2603a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate
261efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    public void finishRestore() {
262efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "finishRestore()");
2633a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate    }
2649bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate}
265