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;
2174318c98a0f67e042815798f85c75eb7f14390e1Christopher Tateimport android.app.backup.BackupTransport;
226a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tateimport android.app.backup.RestoreDescription;
234528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tateimport android.app.backup.RestoreSet;
24cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tateimport android.content.ComponentName;
259bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.content.Context;
26a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tateimport android.content.Intent;
279bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.content.pm.PackageInfo;
289bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.os.Environment;
299bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.os.ParcelFileDescriptor;
30ebab0ae105f1a6df593a4bc2549fae3ee8b2ade4rpcraigimport android.os.SELinux;
31f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughesimport android.system.ErrnoException;
32f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughesimport android.system.Os;
33f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughesimport android.system.StructStat;
349bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.util.Log;
359bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
364140faeebbfa23d56068c1862b2913fb62145f4fBrian Carlstromimport com.android.org.bouncycastle.util.encoders.Base64;
37e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate
38824392bdbdd0d593c4bd2d26f197bb0009ff75c9Christopher Tateimport libcore.io.IoUtils;
39824392bdbdd0d593c4bd2d26f197bb0009ff75c9Christopher Tate
409ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tateimport java.io.BufferedOutputStream;
419bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.File;
429bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileInputStream;
439ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tateimport java.io.FileNotFoundException;
449bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileOutputStream;
459bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.IOException;
46adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tateimport java.util.ArrayList;
479ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tateimport java.util.Arrays;
48adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tateimport java.util.Collections;
499ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tateimport java.util.HashSet;
509ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tateimport java.util.List;
519bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
52f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughesimport static android.system.OsConstants.*;
53b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate
549bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate/**
559bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate * Backup transport for stashing stuff into a known location on disk, and
569bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate * later restoring from there.  For testing only.
579bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate */
589bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
5974318c98a0f67e042815798f85c75eb7f14390e1Christopher Tatepublic class LocalTransport extends BackupTransport {
609bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    private static final String TAG = "LocalTransport";
61a50cd8d4264ca98e19b858596de3a223ba6bf42eEd Heyl    private static final boolean DEBUG = false;
629bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
635cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate    private static final String TRANSPORT_DIR_NAME
645cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate            = "com.android.internal.backup.LocalTransport";
655cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
66a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    private static final String TRANSPORT_DESTINATION_STRING
67a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate            = "Backing up to debug-only private cache";
68a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate
699679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    private static final String TRANSPORT_DATA_MANAGEMENT_LABEL
709679410db578e179c7559e7a52bb21c8082e9631Christopher Tate            = "";
719679410db578e179c7559e7a52bb21c8082e9631Christopher Tate
726a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private static final String INCREMENTAL_DIR = "_delta";
736a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private static final String FULL_DATA_DIR = "_full";
746a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
75adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    // The currently-active restore set always has the same (nonzero!) token
76adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    private static final long CURRENT_SET_TOKEN = 1;
7750c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate
789bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    private Context mContext;
799bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
80adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN));
816a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, INCREMENTAL_DIR);
826a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private File mCurrentSetFullDir = new File(mCurrentSetDir, FULL_DATA_DIR);
83adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
84efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    private PackageInfo[] mRestorePackages = null;
85efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    private int mRestorePackage = -1;  // Index into mRestorePackages
866a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private int mRestoreType;
876a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private File mRestoreSetDir;
886a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private File mRestoreSetIncrementalDir;
896a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private File mRestoreSetFullDir;
90adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    private long mRestoreToken;
919bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
929ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    // Additional bookkeeping for full backup
939ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private String mFullTargetPackage;
949ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private ParcelFileDescriptor mSocket;
959ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private FileInputStream mSocketInputStream;
969ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private BufferedOutputStream mFullBackupOutputStream;
979ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private byte[] mFullBackupBuffer;
989ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
999ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private File mFullRestoreSetDir;
1009ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private HashSet<String> mFullRestorePackages;
1015a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    private FileInputStream mCurFullRestoreStream;
1025a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    private FileOutputStream mFullRestoreSocketStream;
1035a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    private byte[] mFullRestoreBuffer;
1049bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1059bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    public LocalTransport(Context context) {
1069bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        mContext = context;
107adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        mCurrentSetDir.mkdirs();
1089ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        mCurrentSetFullDir.mkdir();
1099ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        mCurrentSetIncrementalDir.mkdir();
110adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        if (!SELinux.restorecon(mCurrentSetDir)) {
111adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            Log.e(TAG, "SELinux restorecon failed for " + mCurrentSetDir);
112ebab0ae105f1a6df593a4bc2549fae3ee8b2ade4rpcraig        }
1139bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
1149bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1155a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
116cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tate    public String name() {
117cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tate        return new ComponentName(mContext, this.getClass()).flattenToShortString();
118cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tate    }
119cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tate
1205a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
121a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    public Intent configurationIntent() {
122a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        // The local transport is not user-configurable
123a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        return null;
124a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    }
125a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate
1265a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
127a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    public String currentDestinationString() {
128a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        return TRANSPORT_DESTINATION_STRING;
1299679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    }
1309679410db578e179c7559e7a52bb21c8082e9631Christopher Tate
1319679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    public Intent dataManagementIntent() {
1329679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        // The local transport does not present a data-management UI
1339679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        // TODO: consider adding simple UI to wipe the archives entirely,
1349679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        // for cleaning up the cache partition.
1359679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        return null;
1369679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    }
1379679410db578e179c7559e7a52bb21c8082e9631Christopher Tate
1389679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    public String dataManagementLabel() {
1399679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        return TRANSPORT_DATA_MANAGEMENT_LABEL;
140a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    }
1415cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
1425a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
1430144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public String transportDirName() {
1445cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate        return TRANSPORT_DIR_NAME;
1455cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate    }
1465cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
1475a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
1480144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public long requestBackupTime() {
1499bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        // any time is a good time for local backup
1509bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        return 0;
1519bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
1529bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1535a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
1540144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int initializeDevice() {
1550144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        if (DEBUG) Log.v(TAG, "wiping all data");
156adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        deleteContents(mCurrentSetDir);
1575a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
1580144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    }
1590144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor
1605a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
1610144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
162b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate        if (DEBUG) {
163b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate            try {
164f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughes            StructStat ss = Os.fstat(data.getFileDescriptor());
165b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate            Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName
166b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                    + " size=" + ss.st_size);
167b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate            } catch (ErrnoException e) {
168b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                Log.w(TAG, "Unable to stat input file in performBackup() on "
169b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                        + packageInfo.packageName);
170b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate            }
171b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate        }
1729bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1739ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        File packageDir = new File(mCurrentSetIncrementalDir, packageInfo.packageName);
1742fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        packageDir.mkdirs();
1759bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1762fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // Each 'record' in the restore set is kept in its own file, named by
1772fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // the record key.  Wind through the data file, extracting individual
1782fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // record operations and building a set of all the updates to apply
1792fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // in this update.
1802fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor());
1812fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        try {
1822fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            int bufSize = 512;
1832fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            byte[] buf = new byte[bufSize];
1842fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            while (changeSet.readNextHeader()) {
1852fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate                String key = changeSet.getKey();
1865d605dc56b036232e885f6ec36b888b729673060Joe Onorato                String base64Key = new String(Base64.encode(key.getBytes()));
1875d605dc56b036232e885f6ec36b888b729673060Joe Onorato                File entityFile = new File(packageDir, base64Key);
1885d605dc56b036232e885f6ec36b888b729673060Joe Onorato
1892fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate                int dataSize = changeSet.getDataSize();
190e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate
191e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate                if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize
192e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate                        + " key64=" + base64Key);
1932fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate
1945d605dc56b036232e885f6ec36b888b729673060Joe Onorato                if (dataSize >= 0) {
1951afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                    if (entityFile.exists()) {
1961afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                        entityFile.delete();
1971afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                    }
1985d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    FileOutputStream entity = new FileOutputStream(entityFile);
1995d605dc56b036232e885f6ec36b888b729673060Joe Onorato
2005d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    if (dataSize > bufSize) {
2015d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        bufSize = dataSize;
2025d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        buf = new byte[bufSize];
2035d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    }
2045d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    changeSet.readEntityData(buf, 0, dataSize);
205b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                    if (DEBUG) {
206b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                        try {
207f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughes                            long cur = Os.lseek(data.getFileDescriptor(), 0, SEEK_CUR);
208b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                            Log.v(TAG, "  read entity data; new pos=" + cur);
209b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                        }
210b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                        catch (ErrnoException e) {
211b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                            Log.w(TAG, "Unable to stat input file in performBackup() on "
212b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                                    + packageInfo.packageName);
213b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                        }
214b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                    }
2155d605dc56b036232e885f6ec36b888b729673060Joe Onorato
2165d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    try {
2175d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        entity.write(buf, 0, dataSize);
2185d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    } catch (IOException e) {
219efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                        Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
2205a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                        return TRANSPORT_ERROR;
2215d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    } finally {
2225d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        entity.close();
2235d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    }
2245d605dc56b036232e885f6ec36b888b729673060Joe Onorato                } else {
2255d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    entityFile.delete();
2262fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate                }
2272fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
2285a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_OK;
2292fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        } catch (IOException e) {
2302fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            // oops, something went wrong.  abort the operation and return error.
231efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            Log.v(TAG, "Exception reading backup input:", e);
2325a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
2332fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        }
234efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    }
235ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate
23625a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    // Deletes the contents but not the given directory
23725a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    private void deleteContents(File dirname) {
23825a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        File[] contents = dirname.listFiles();
23925a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        if (contents != null) {
24025a747f5c47f25c1a18961b03507f309b84924feChristopher Tate            for (File f : contents) {
24125a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                if (f.isDirectory()) {
24225a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    // delete the directory's contents then fall through
24325a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    // and delete the directory itself.
24425a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    deleteContents(f);
24525a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                }
24625a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                f.delete();
24725a747f5c47f25c1a18961b03507f309b84924feChristopher Tate            }
24825a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        }
24925a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    }
25025a747f5c47f25c1a18961b03507f309b84924feChristopher Tate
2515a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
2520144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int clearBackupData(PackageInfo packageInfo) {
253ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate        if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
254ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate
2559ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        File packageDir = new File(mCurrentSetIncrementalDir, packageInfo.packageName);
2560abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate        final File[] fileset = packageDir.listFiles();
2570abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate        if (fileset != null) {
2580abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            for (File f : fileset) {
2590abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate                f.delete();
2600abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            }
2610abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            packageDir.delete();
262ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate        }
2639ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
2649ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        packageDir = new File(mCurrentSetFullDir, packageInfo.packageName);
2659ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        final File[] tarballs = packageDir.listFiles();
2669ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (tarballs != null) {
2679ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            for (File f : tarballs) {
2689ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                f.delete();
2699ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            }
2709ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            packageDir.delete();
2719ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
2729ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
2735a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
274ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate    }
2759bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
2765a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
2770144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int finishBackup() {
278e079264b981d87c648921185528b553a86ae353dChristopher Tate        if (DEBUG) Log.v(TAG, "finishBackup() of " + mFullTargetPackage);
279e079264b981d87c648921185528b553a86ae353dChristopher Tate        return tearDownFullBackup();
280e079264b981d87c648921185528b553a86ae353dChristopher Tate    }
281e079264b981d87c648921185528b553a86ae353dChristopher Tate
282e079264b981d87c648921185528b553a86ae353dChristopher Tate    // ------------------------------------------------------------------------------------
283e079264b981d87c648921185528b553a86ae353dChristopher Tate    // Full backup handling
284e079264b981d87c648921185528b553a86ae353dChristopher Tate
285e079264b981d87c648921185528b553a86ae353dChristopher Tate    private int tearDownFullBackup() {
2869ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (mSocket != null) {
2879ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            try {
2889ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                mFullBackupOutputStream.flush();
2899ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                mFullBackupOutputStream.close();
2909ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                mSocketInputStream = null;
2919ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                mFullTargetPackage = null;
2929ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                mSocket.close();
2939ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            } catch (IOException e) {
29489101f7fe89134a609459e80f779c1a5114e562aChristopher Tate                if (DEBUG) {
295e079264b981d87c648921185528b553a86ae353dChristopher Tate                    Log.w(TAG, "Exception caught in tearDownFullBackup()", e);
29689101f7fe89134a609459e80f779c1a5114e562aChristopher Tate                }
2975a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                return TRANSPORT_ERROR;
2989ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            } finally {
2999ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                mSocket = null;
3009ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            }
3019ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3025a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
3039bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
3049bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
305e079264b981d87c648921185528b553a86ae353dChristopher Tate    private File tarballFile(String pkgName) {
306e079264b981d87c648921185528b553a86ae353dChristopher Tate        return new File(mCurrentSetFullDir, pkgName);
307e079264b981d87c648921185528b553a86ae353dChristopher Tate    }
3085a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
3095a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
3109ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    public long requestFullBackupTime() {
3119ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        return 0;
3129ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
3139ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
3145a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
3159ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) {
3169ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (mSocket != null) {
3179ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            Log.e(TAG, "Attempt to initiate full backup while one is in progress");
3185a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
3199ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3209ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
3219ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (DEBUG) {
3229ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            Log.i(TAG, "performFullBackup : " + targetPackage);
3239ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3249ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
3259ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // We know a priori that we run in the system process, so we need to make
3269ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // sure to dup() our own copy of the socket fd.  Transports which run in
3279ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // their own processes must not do this.
3289ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        try {
3299ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
3309ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            mSocketInputStream = new FileInputStream(mSocket.getFileDescriptor());
3319ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        } catch (IOException e) {
3329ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            Log.e(TAG, "Unable to process socket for full backup");
3335a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
3349ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3359ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
3369ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        mFullTargetPackage = targetPackage.packageName;
3379ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        FileOutputStream tarstream;
3389ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        try {
339e079264b981d87c648921185528b553a86ae353dChristopher Tate            File tarball = tarballFile(mFullTargetPackage);
3409ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            tarstream = new FileOutputStream(tarball);
3419ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        } catch (FileNotFoundException e) {
3425a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
3439ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3449ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        mFullBackupOutputStream = new BufferedOutputStream(tarstream);
3459ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        mFullBackupBuffer = new byte[4096];
3469ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
3475a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
3489ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
3499ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
3505a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
3519ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    public int sendBackupData(int numBytes) {
3529ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (mFullBackupBuffer == null) {
3539ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            Log.w(TAG, "Attempted sendBackupData before performFullBackup");
3545a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
3559ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3569ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
3579ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (numBytes > mFullBackupBuffer.length) {
3589ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            mFullBackupBuffer = new byte[numBytes];
3599ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3609ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        while (numBytes > 0) {
3619ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            try {
3629ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            int nRead = mSocketInputStream.read(mFullBackupBuffer, 0, numBytes);
3639ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            if (nRead < 0) {
3649ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                // Something went wrong if we expect data but saw EOD
3659ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                Log.w(TAG, "Unexpected EOD; failing backup");
3665a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                return TRANSPORT_ERROR;
3679ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            }
3689ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            mFullBackupOutputStream.write(mFullBackupBuffer, 0, nRead);
3699ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            numBytes -= nRead;
3709ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            } catch (IOException e) {
3719ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                Log.e(TAG, "Error handling backup data for " + mFullTargetPackage);
3725a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                return TRANSPORT_ERROR;
3739ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            }
3749ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3755a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
3769ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
3779ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
378e079264b981d87c648921185528b553a86ae353dChristopher Tate    // For now we can't roll back, so just tear everything down.
379e079264b981d87c648921185528b553a86ae353dChristopher Tate    @Override
380e079264b981d87c648921185528b553a86ae353dChristopher Tate    public void cancelFullBackup() {
381e079264b981d87c648921185528b553a86ae353dChristopher Tate        if (DEBUG) {
382e079264b981d87c648921185528b553a86ae353dChristopher Tate            Log.i(TAG, "Canceling full backup of " + mFullTargetPackage);
383e079264b981d87c648921185528b553a86ae353dChristopher Tate        }
384e079264b981d87c648921185528b553a86ae353dChristopher Tate        File archive = tarballFile(mFullTargetPackage);
385e079264b981d87c648921185528b553a86ae353dChristopher Tate        tearDownFullBackup();
386e079264b981d87c648921185528b553a86ae353dChristopher Tate        if (archive.exists()) {
387e079264b981d87c648921185528b553a86ae353dChristopher Tate            archive.delete();
388e079264b981d87c648921185528b553a86ae353dChristopher Tate        }
389e079264b981d87c648921185528b553a86ae353dChristopher Tate    }
390e079264b981d87c648921185528b553a86ae353dChristopher Tate
3919ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    // ------------------------------------------------------------------------------------
3929bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    // Restore handling
39351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 };
3945a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
3955a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
39674318c98a0f67e042815798f85c75eb7f14390e1Christopher Tate    public RestoreSet[] getAvailableRestoreSets() {
397adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        long[] existing = new long[POSSIBLE_SETS.length + 1];
398adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        int num = 0;
399adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
4009ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // see which possible non-current sets exist...
401adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        for (long token : POSSIBLE_SETS) {
402adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            if ((new File(mDataDir, Long.toString(token))).exists()) {
403adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate                existing[num++] = token;
404adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            }
405adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
4069ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // ...and always the currently-active set last
407adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        existing[num++] = CURRENT_SET_TOKEN;
408adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
409adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        RestoreSet[] available = new RestoreSet[num];
410adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        for (int i = 0; i < available.length; i++) {
411adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            available[i] = new RestoreSet("Local disk image", "flash", existing[i]);
412adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
413adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        return available;
4149bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
4159bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
4165a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
41750c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    public long getCurrentRestoreSet() {
418adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // The current restore set always has the same token
419adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        return CURRENT_SET_TOKEN;
42050c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    }
42150c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate
4225a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
4230144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int startRestore(long token, PackageInfo[] packages) {
42451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        if (DEBUG) Log.v(TAG, "start restore " + token + " : " + packages.length
42551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                + " matching packages");
426efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        mRestorePackages = packages;
427efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        mRestorePackage = -1;
428adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        mRestoreToken = token;
4296a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        mRestoreSetDir = new File(mDataDir, Long.toString(token));
4306a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        mRestoreSetIncrementalDir = new File(mRestoreSetDir, INCREMENTAL_DIR);
4316a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        mRestoreSetFullDir = new File(mRestoreSetDir, FULL_DATA_DIR);
4325a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
433efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    }
4349bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
4356a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    @Override
4366a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    public RestoreDescription nextRestorePackage() {
437efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
4386a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
4396a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        boolean found = false;
440efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        while (++mRestorePackage < mRestorePackages.length) {
441efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            String name = mRestorePackages[mRestorePackage].packageName;
4426a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
4436a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            // If we have key/value data for this package, deliver that
444a9b91864a1aedd71eaaaa43ee078cf93922289f3Christopher Tate            // skip packages where we have a data dir but no actual contents
4456a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            String[] contents = (new File(mRestoreSetIncrementalDir, name)).list();
446a9b91864a1aedd71eaaaa43ee078cf93922289f3Christopher Tate            if (contents != null && contents.length > 0) {
4476a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                if (DEBUG) Log.v(TAG, "  nextRestorePackage(TYPE_KEY_VALUE) = " + name);
4486a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                mRestoreType = RestoreDescription.TYPE_KEY_VALUE;
4496a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                found = true;
4506a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            }
4516a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
4526a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            if (!found) {
4536a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                // No key/value data; check for [non-empty] full data
4546a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                File maybeFullData = new File(mRestoreSetFullDir, name);
4556a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                if (maybeFullData.length() > 0) {
4566a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                    if (DEBUG) Log.v(TAG, "  nextRestorePackage(TYPE_FULL_STREAM) = " + name);
4576a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                    mRestoreType = RestoreDescription.TYPE_FULL_STREAM;
4585a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                    mCurFullRestoreStream = null;   // ensure starting from the ground state
4596a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                    found = true;
4606a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                }
4616a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            }
4626a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
4636a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            if (found) {
4646a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                return new RestoreDescription(name, mRestoreType);
4652fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
4669bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        }
4679bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
468efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "  no more packages to restore");
4696a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        return RestoreDescription.NO_MORE_PACKAGES;
4709bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
4719bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
4725a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
4730144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int getRestoreData(ParcelFileDescriptor outFd) {
474efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
475efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
4766a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        if (mRestoreType != RestoreDescription.TYPE_KEY_VALUE) {
4776a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            throw new IllegalStateException("getRestoreData(fd) for non-key/value dataset");
4786a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        }
47951fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        File packageDir = new File(mRestoreSetIncrementalDir,
48051fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                mRestorePackages[mRestorePackage].packageName);
4819bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
4822fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // The restore set is the concatenation of the individual record blobs,
483adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // each of which is a file in the package's directory.  We return the
484adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // data in lexical order sorted by key, so that apps which use synthetic
485adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // keys like BLOB_1, BLOB_2, etc will see the date in the most obvious
486adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // order.
487adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        ArrayList<DecodedFilename> blobs = contentsByKey(packageDir);
4880144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        if (blobs == null) {  // nextRestorePackage() ensures the dir exists, so this is an error
489adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            Log.e(TAG, "No keys for package: " + packageDir);
4905a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
491efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        }
492efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor
493efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        // We expect at least some data if the directory exists in the first place
494adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        if (DEBUG) Log.v(TAG, "  getRestoreData() found " + blobs.size() + " key files");
495efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
496efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        try {
497adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            for (DecodedFilename keyEntry : blobs) {
498adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate                File f = keyEntry.file;
499efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                FileInputStream in = new FileInputStream(f);
500efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                try {
501efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    int size = (int) f.length();
502efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    byte[] buf = new byte[size];
503efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    in.read(buf);
504adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate                    if (DEBUG) Log.v(TAG, "    ... key=" + keyEntry.key + " size=" + size);
505adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate                    out.writeEntityHeader(keyEntry.key, size);
506efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    out.writeEntityData(buf, size);
507efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                } finally {
508efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    in.close();
5098e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate                }
5102fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
5115a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_OK;
512efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        } catch (IOException e) {
513efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            Log.e(TAG, "Unable to read backup records", e);
5145a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
5152fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        }
5169bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
5173a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate
518adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    static class DecodedFilename implements Comparable<DecodedFilename> {
519adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        public File file;
520adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        public String key;
521adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
522adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        public DecodedFilename(File f) {
523adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            file = f;
524adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            key = new String(Base64.decode(f.getName()));
525adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
526adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
527adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        @Override
528adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        public int compareTo(DecodedFilename other) {
529adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            // sorts into ascending lexical order by decoded key
530adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            return key.compareTo(other.key);
531adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
532adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    }
533adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
534adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    // Return a list of the files in the given directory, sorted lexically by
535adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    // the Base64-decoded file name, not by the on-disk filename
536adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    private ArrayList<DecodedFilename> contentsByKey(File dir) {
537adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        File[] allFiles = dir.listFiles();
538adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        if (allFiles == null || allFiles.length == 0) {
539adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            return null;
540adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
541adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
542adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // Decode the filenames into keys then sort lexically by key
543adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        ArrayList<DecodedFilename> contents = new ArrayList<DecodedFilename>();
544adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        for (File f : allFiles) {
545adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            contents.add(new DecodedFilename(f));
546adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
547adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        Collections.sort(contents);
548adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        return contents;
549adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    }
550adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
5515a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
552efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    public void finishRestore() {
553efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "finishRestore()");
5545a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        if (mRestoreType == RestoreDescription.TYPE_FULL_STREAM) {
5555a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            resetFullRestoreState();
5565a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
5575a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mRestoreType = 0;
5583a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate    }
5599ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
5609ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    // ------------------------------------------------------------------------------------
5619ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    // Full restore handling
5629ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
5635a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    private void resetFullRestoreState() {
564824392bdbdd0d593c4bd2d26f197bb0009ff75c9Christopher Tate        IoUtils.closeQuietly(mCurFullRestoreStream);
5655a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mCurFullRestoreStream = null;
5665a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mFullRestoreSocketStream = null;
5675a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mFullRestoreBuffer = null;
5689ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
5699ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
5709ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    /**
5719ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * Ask the transport to provide data for the "current" package being restored.  The
5729ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * transport then writes some data to the socket supplied to this call, and returns
5739ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * the number of bytes written.  The system will then read that many bytes and
5749ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * stream them to the application's agent for restore, then will call this method again
5759ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * to receive the next chunk of the archive.  This sequence will be repeated until the
5769ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * transport returns zero indicating that all of the package's data has been delivered
5779ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * (or returns a negative value indicating some sort of hard error condition at the
5789ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * transport level).
5799ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *
5809ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * <p>After this method returns zero, the system will then call
5819ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * {@link #getNextFullRestorePackage()} to begin the restore process for the next
5829ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * application, and the sequence begins again.
5839ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *
5849ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * @param socket The file descriptor that the transport will use for delivering the
5859ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    streamed archive.
5869ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * @return 0 when no more data for the current package is available.  A positive value
5879ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    indicates the presence of that much data to be delivered to the app.  A negative
5889ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR},
5899ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    indicating a fatal error condition that precludes further restore operations
5909ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    on the current dataset.
5919ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     */
5925a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
5939ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
5945a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        if (mRestoreType != RestoreDescription.TYPE_FULL_STREAM) {
5955a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            throw new IllegalStateException("Asked for full restore data for non-stream package");
5965a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
5975a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
5985a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        // first chunk?
5995a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        if (mCurFullRestoreStream == null) {
6005a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            final String name = mRestorePackages[mRestorePackage].packageName;
6015a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            if (DEBUG) Log.i(TAG, "Starting full restore of " + name);
6025a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            File dataset = new File(mRestoreSetFullDir, name);
6035a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            try {
6045a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                mCurFullRestoreStream = new FileInputStream(dataset);
6055a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            } catch (IOException e) {
6065a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // If we can't open the target package's tarball, we return the single-package
6075a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // error code and let the caller go on to the next package.
6085a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                Log.e(TAG, "Unable to read archive for " + name);
6095a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                return TRANSPORT_PACKAGE_REJECTED;
6105a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            }
6115a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            mFullRestoreSocketStream = new FileOutputStream(socket.getFileDescriptor());
61289101f7fe89134a609459e80f779c1a5114e562aChristopher Tate            mFullRestoreBuffer = new byte[2*1024];
6135a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
6145a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
6155a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        int nRead;
6165a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        try {
6175a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            nRead = mCurFullRestoreStream.read(mFullRestoreBuffer);
6185a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            if (nRead < 0) {
6195a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // EOF: tell the caller we're done
6205a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                nRead = NO_MORE_DATA;
6215a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            } else if (nRead == 0) {
6225a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // This shouldn't happen when reading a FileInputStream; we should always
6235a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // get either a positive nonzero byte count or -1.  Log the situation and
6245a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // treat it as EOF.
6255a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                Log.w(TAG, "read() of archive file returned 0; treating as EOF");
6265a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                nRead = NO_MORE_DATA;
6275a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            } else {
6285a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                if (DEBUG) {
6295a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                    Log.i(TAG, "   delivering restore chunk: " + nRead);
6305a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                }
6315a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                mFullRestoreSocketStream.write(mFullRestoreBuffer, 0, nRead);
6325a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            }
6335a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        } catch (IOException e) {
6345a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;  // Hard error accessing the file; shouldn't happen
6355a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        } finally {
6365a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            // Most transports will need to explicitly close 'socket' here, but this transport
6375a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            // is in the same process as the caller so it can leave it up to the backup manager
6385a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            // to manage both socket fds.
6395a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
6405a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
6415a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return nRead;
6429ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
6435a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
6445a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    /**
6455a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM}
6465a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * data for restore, it will invoke this method to tell the transport that it should
6475a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * abandon the data download for the current package.  The OS will then either call
6485a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * {@link #nextRestorePackage()} again to move on to restoring the next package in the
6495a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * set being iterated over, or will call {@link #finishRestore()} to shut down the restore
6505a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * operation.
6515a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     *
6525a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the
6535a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     *    current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious
6545a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     *    transport-level failure.  If the transport reports an error here, the entire restore
6555a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     *    operation will immediately be finished with no further attempts to restore app data.
6565a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     */
6575a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
6585a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    public int abortFullRestore() {
6595a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        if (mRestoreType != RestoreDescription.TYPE_FULL_STREAM) {
6605a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            throw new IllegalStateException("abortFullRestore() but not currently restoring");
6615a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
6625a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        resetFullRestoreState();
6635a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mRestoreType = 0;
6645a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
6655a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    }
6665a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
6679bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate}
668