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;
30f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughesimport android.system.ErrnoException;
31f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughesimport android.system.Os;
32f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughesimport android.system.StructStat;
339bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.util.Log;
349bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
354140faeebbfa23d56068c1862b2913fb62145f4fBrian Carlstromimport com.android.org.bouncycastle.util.encoders.Base64;
36e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate
37824392bdbdd0d593c4bd2d26f197bb0009ff75c9Christopher Tateimport libcore.io.IoUtils;
38824392bdbdd0d593c4bd2d26f197bb0009ff75c9Christopher Tate
399ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tateimport java.io.BufferedOutputStream;
409bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.File;
419bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileInputStream;
429ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tateimport java.io.FileNotFoundException;
439bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileOutputStream;
449bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.IOException;
45adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tateimport java.util.ArrayList;
46adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tateimport java.util.Collections;
479310e4285b3fc951c3524d040726d1161015562cChristopher Tateimport static android.system.OsConstants.SEEK_CUR;
48b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate
499bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate/**
509bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate * Backup transport for stashing stuff into a known location on disk, and
519bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate * later restoring from there.  For testing only.
529bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate */
539bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
5474318c98a0f67e042815798f85c75eb7f14390e1Christopher Tatepublic class LocalTransport extends BackupTransport {
559bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    private static final String TAG = "LocalTransport";
56a50cd8d4264ca98e19b858596de3a223ba6bf42eEd Heyl    private static final boolean DEBUG = false;
579bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
585cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate    private static final String TRANSPORT_DIR_NAME
595cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate            = "com.android.internal.backup.LocalTransport";
605cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
61a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    private static final String TRANSPORT_DESTINATION_STRING
62a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate            = "Backing up to debug-only private cache";
63a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate
649679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    private static final String TRANSPORT_DATA_MANAGEMENT_LABEL
659679410db578e179c7559e7a52bb21c8082e9631Christopher Tate            = "";
669679410db578e179c7559e7a52bb21c8082e9631Christopher Tate
676a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private static final String INCREMENTAL_DIR = "_delta";
686a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private static final String FULL_DATA_DIR = "_full";
696a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
70adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    // The currently-active restore set always has the same (nonzero!) token
71adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    private static final long CURRENT_SET_TOKEN = 1;
7250c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate
73872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov    // Full backup size quota is set to reasonable value.
74872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov    private static final long FULL_BACKUP_SIZE_QUOTA = 25 * 1024 * 1024;
75872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov
769bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    private Context mContext;
779bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
78adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN));
796a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, INCREMENTAL_DIR);
806a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private File mCurrentSetFullDir = new File(mCurrentSetDir, FULL_DATA_DIR);
81adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
82efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    private PackageInfo[] mRestorePackages = null;
83efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    private int mRestorePackage = -1;  // Index into mRestorePackages
846a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private int mRestoreType;
856a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private File mRestoreSetDir;
866a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private File mRestoreSetIncrementalDir;
876a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private File mRestoreSetFullDir;
889bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
899ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    // Additional bookkeeping for full backup
909ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private String mFullTargetPackage;
919ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private ParcelFileDescriptor mSocket;
929ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private FileInputStream mSocketInputStream;
939ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private BufferedOutputStream mFullBackupOutputStream;
949ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private byte[] mFullBackupBuffer;
95872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov    private long mFullBackupSize;
969ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
975a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    private FileInputStream mCurFullRestoreStream;
985a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    private FileOutputStream mFullRestoreSocketStream;
995a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    private byte[] mFullRestoreBuffer;
1009bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
101de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate    private void makeDataDirs() {
102adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        mCurrentSetDir.mkdirs();
103de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate        mCurrentSetFullDir.mkdir();
104de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate        mCurrentSetIncrementalDir.mkdir();
105de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate    }
106de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate
107de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate    public LocalTransport(Context context) {
108de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate        mContext = context;
109de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate        makeDataDirs();
1109bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
1119bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1125a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
113cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tate    public String name() {
114cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tate        return new ComponentName(mContext, this.getClass()).flattenToShortString();
115cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tate    }
116cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tate
1175a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
118a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    public Intent configurationIntent() {
119a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        // The local transport is not user-configurable
120a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        return null;
121a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    }
122a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate
1235a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
124a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    public String currentDestinationString() {
125a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        return TRANSPORT_DESTINATION_STRING;
1269679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    }
1279679410db578e179c7559e7a52bb21c8082e9631Christopher Tate
1289679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    public Intent dataManagementIntent() {
1299679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        // The local transport does not present a data-management UI
1309679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        // TODO: consider adding simple UI to wipe the archives entirely,
1319679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        // for cleaning up the cache partition.
1329679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        return null;
1339679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    }
1349679410db578e179c7559e7a52bb21c8082e9631Christopher Tate
1359679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    public String dataManagementLabel() {
1369679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        return TRANSPORT_DATA_MANAGEMENT_LABEL;
137a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    }
1385cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
1395a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
1400144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public String transportDirName() {
1415cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate        return TRANSPORT_DIR_NAME;
1425cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate    }
1435cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
1445a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
1450144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public long requestBackupTime() {
1469bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        // any time is a good time for local backup
1479bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        return 0;
1489bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
1499bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1505a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
1510144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int initializeDevice() {
1520144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        if (DEBUG) Log.v(TAG, "wiping all data");
153adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        deleteContents(mCurrentSetDir);
154de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate        makeDataDirs();
1555a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
1560144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    }
1570144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor
1585a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
1590144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
160b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate        if (DEBUG) {
161b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate            try {
162f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughes            StructStat ss = Os.fstat(data.getFileDescriptor());
163b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate            Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName
164b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                    + " size=" + ss.st_size);
165b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate            } catch (ErrnoException e) {
166b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                Log.w(TAG, "Unable to stat input file in performBackup() on "
167b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                        + packageInfo.packageName);
168b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate            }
169b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate        }
1709bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1719ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        File packageDir = new File(mCurrentSetIncrementalDir, packageInfo.packageName);
1722fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        packageDir.mkdirs();
1739bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1742fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // Each 'record' in the restore set is kept in its own file, named by
1752fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // the record key.  Wind through the data file, extracting individual
1762fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // record operations and building a set of all the updates to apply
1772fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // in this update.
1782fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor());
1792fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        try {
1802fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            int bufSize = 512;
1812fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            byte[] buf = new byte[bufSize];
1822fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            while (changeSet.readNextHeader()) {
1832fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate                String key = changeSet.getKey();
1845d605dc56b036232e885f6ec36b888b729673060Joe Onorato                String base64Key = new String(Base64.encode(key.getBytes()));
1855d605dc56b036232e885f6ec36b888b729673060Joe Onorato                File entityFile = new File(packageDir, base64Key);
1865d605dc56b036232e885f6ec36b888b729673060Joe Onorato
1872fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate                int dataSize = changeSet.getDataSize();
188e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate
189e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate                if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize
190e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate                        + " key64=" + base64Key);
1912fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate
1925d605dc56b036232e885f6ec36b888b729673060Joe Onorato                if (dataSize >= 0) {
1931afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                    if (entityFile.exists()) {
1941afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                        entityFile.delete();
1951afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                    }
1965d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    FileOutputStream entity = new FileOutputStream(entityFile);
1975d605dc56b036232e885f6ec36b888b729673060Joe Onorato
1985d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    if (dataSize > bufSize) {
1995d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        bufSize = dataSize;
2005d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        buf = new byte[bufSize];
2015d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    }
2025d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    changeSet.readEntityData(buf, 0, dataSize);
203b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                    if (DEBUG) {
204b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                        try {
205f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughes                            long cur = Os.lseek(data.getFileDescriptor(), 0, SEEK_CUR);
206b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                            Log.v(TAG, "  read entity data; new pos=" + cur);
207b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                        }
208b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                        catch (ErrnoException e) {
209b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                            Log.w(TAG, "Unable to stat input file in performBackup() on "
210b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                                    + packageInfo.packageName);
211b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                        }
212b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                    }
2135d605dc56b036232e885f6ec36b888b729673060Joe Onorato
2145d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    try {
2155d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        entity.write(buf, 0, dataSize);
2165d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    } catch (IOException e) {
217efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                        Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
2185a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                        return TRANSPORT_ERROR;
2195d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    } finally {
2205d605dc56b036232e885f6ec36b888b729673060Joe Onorato                        entity.close();
2215d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    }
2225d605dc56b036232e885f6ec36b888b729673060Joe Onorato                } else {
2235d605dc56b036232e885f6ec36b888b729673060Joe Onorato                    entityFile.delete();
2242fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate                }
2252fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
2265a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_OK;
2272fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        } catch (IOException e) {
2282fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            // oops, something went wrong.  abort the operation and return error.
229efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            Log.v(TAG, "Exception reading backup input:", e);
2305a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
2312fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        }
232efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    }
233ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate
23425a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    // Deletes the contents but not the given directory
23525a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    private void deleteContents(File dirname) {
23625a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        File[] contents = dirname.listFiles();
23725a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        if (contents != null) {
23825a747f5c47f25c1a18961b03507f309b84924feChristopher Tate            for (File f : contents) {
23925a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                if (f.isDirectory()) {
24025a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    // delete the directory's contents then fall through
24125a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    // and delete the directory itself.
24225a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    deleteContents(f);
24325a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                }
24425a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                f.delete();
24525a747f5c47f25c1a18961b03507f309b84924feChristopher Tate            }
24625a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        }
24725a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    }
24825a747f5c47f25c1a18961b03507f309b84924feChristopher Tate
2495a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
2500144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int clearBackupData(PackageInfo packageInfo) {
251ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate        if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
252ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate
2539ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        File packageDir = new File(mCurrentSetIncrementalDir, packageInfo.packageName);
2540abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate        final File[] fileset = packageDir.listFiles();
2550abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate        if (fileset != null) {
2560abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            for (File f : fileset) {
2570abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate                f.delete();
2580abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            }
2590abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            packageDir.delete();
260ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate        }
2619ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
2629ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        packageDir = new File(mCurrentSetFullDir, packageInfo.packageName);
2639ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        final File[] tarballs = packageDir.listFiles();
2649ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (tarballs != null) {
2659ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            for (File f : tarballs) {
2669ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                f.delete();
2679ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            }
2689ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            packageDir.delete();
2699ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
2709ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
2715a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
272ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate    }
2739bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
2745a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
2750144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int finishBackup() {
276e079264b981d87c648921185528b553a86ae353dChristopher Tate        if (DEBUG) Log.v(TAG, "finishBackup() of " + mFullTargetPackage);
277e079264b981d87c648921185528b553a86ae353dChristopher Tate        return tearDownFullBackup();
278e079264b981d87c648921185528b553a86ae353dChristopher Tate    }
279e079264b981d87c648921185528b553a86ae353dChristopher Tate
280e079264b981d87c648921185528b553a86ae353dChristopher Tate    // ------------------------------------------------------------------------------------
281e079264b981d87c648921185528b553a86ae353dChristopher Tate    // Full backup handling
282e079264b981d87c648921185528b553a86ae353dChristopher Tate
283e079264b981d87c648921185528b553a86ae353dChristopher Tate    private int tearDownFullBackup() {
2849ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (mSocket != null) {
2859ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            try {
2869310e4285b3fc951c3524d040726d1161015562cChristopher Tate                if (mFullBackupOutputStream != null) {
2879310e4285b3fc951c3524d040726d1161015562cChristopher Tate                    mFullBackupOutputStream.flush();
2889310e4285b3fc951c3524d040726d1161015562cChristopher Tate                    mFullBackupOutputStream.close();
2899310e4285b3fc951c3524d040726d1161015562cChristopher Tate                }
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;
3009310e4285b3fc951c3524d040726d1161015562cChristopher Tate                mFullBackupOutputStream = null;
3019ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            }
3029ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3035a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
3049bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
3059bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
306e079264b981d87c648921185528b553a86ae353dChristopher Tate    private File tarballFile(String pkgName) {
307e079264b981d87c648921185528b553a86ae353dChristopher Tate        return new File(mCurrentSetFullDir, pkgName);
308e079264b981d87c648921185528b553a86ae353dChristopher Tate    }
3095a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
3105a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
3119ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    public long requestFullBackupTime() {
3129ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        return 0;
3139ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
3149ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
3155a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
3169310e4285b3fc951c3524d040726d1161015562cChristopher Tate    public int checkFullBackupSize(long size) {
317872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        int result = TRANSPORT_OK;
3189310e4285b3fc951c3524d040726d1161015562cChristopher Tate        // Decline zero-size "backups"
319872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        if (size <= 0) {
320872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov            result = TRANSPORT_PACKAGE_REJECTED;
321872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        } else if (size > FULL_BACKUP_SIZE_QUOTA) {
322872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov            result = TRANSPORT_QUOTA_EXCEEDED;
323872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        }
3249310e4285b3fc951c3524d040726d1161015562cChristopher Tate        if (result != TRANSPORT_OK) {
3259310e4285b3fc951c3524d040726d1161015562cChristopher Tate            if (DEBUG) {
3269310e4285b3fc951c3524d040726d1161015562cChristopher Tate                Log.v(TAG, "Declining backup of size " + size);
3279310e4285b3fc951c3524d040726d1161015562cChristopher Tate            }
3289310e4285b3fc951c3524d040726d1161015562cChristopher Tate        }
3299310e4285b3fc951c3524d040726d1161015562cChristopher Tate        return result;
3309310e4285b3fc951c3524d040726d1161015562cChristopher Tate    }
3319310e4285b3fc951c3524d040726d1161015562cChristopher Tate
3329310e4285b3fc951c3524d040726d1161015562cChristopher Tate    @Override
3339ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) {
3349ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (mSocket != null) {
3359ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            Log.e(TAG, "Attempt to initiate full backup while one is in progress");
3365a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
3379ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3389ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
3399ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (DEBUG) {
3409ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            Log.i(TAG, "performFullBackup : " + targetPackage);
3419ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3429ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
3439ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // We know a priori that we run in the system process, so we need to make
3449ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // sure to dup() our own copy of the socket fd.  Transports which run in
3459ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // their own processes must not do this.
3469ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        try {
347872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov            mFullBackupSize = 0;
3489ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
3499ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            mSocketInputStream = new FileInputStream(mSocket.getFileDescriptor());
3509ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        } catch (IOException e) {
3519ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            Log.e(TAG, "Unable to process socket for full backup");
3525a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
3539ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3549ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
3559ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        mFullTargetPackage = targetPackage.packageName;
3569ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        mFullBackupBuffer = new byte[4096];
3579ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
3585a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
3599ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
3609ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
3615a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
3629310e4285b3fc951c3524d040726d1161015562cChristopher Tate    public int sendBackupData(final int numBytes) {
3639310e4285b3fc951c3524d040726d1161015562cChristopher Tate        if (mSocket == null) {
3649ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            Log.w(TAG, "Attempted sendBackupData before performFullBackup");
3655a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
3669ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3679ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
368872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        mFullBackupSize += numBytes;
369872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        if (mFullBackupSize > FULL_BACKUP_SIZE_QUOTA) {
370872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov            return TRANSPORT_QUOTA_EXCEEDED;
371872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        }
372872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov
3739ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (numBytes > mFullBackupBuffer.length) {
3749ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            mFullBackupBuffer = new byte[numBytes];
3759ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
3769310e4285b3fc951c3524d040726d1161015562cChristopher Tate
3779310e4285b3fc951c3524d040726d1161015562cChristopher Tate        if (mFullBackupOutputStream == null) {
3789310e4285b3fc951c3524d040726d1161015562cChristopher Tate            FileOutputStream tarstream;
3799310e4285b3fc951c3524d040726d1161015562cChristopher Tate            try {
3809310e4285b3fc951c3524d040726d1161015562cChristopher Tate                File tarball = tarballFile(mFullTargetPackage);
3819310e4285b3fc951c3524d040726d1161015562cChristopher Tate                tarstream = new FileOutputStream(tarball);
3829310e4285b3fc951c3524d040726d1161015562cChristopher Tate            } catch (FileNotFoundException e) {
3839310e4285b3fc951c3524d040726d1161015562cChristopher Tate                return TRANSPORT_ERROR;
3849310e4285b3fc951c3524d040726d1161015562cChristopher Tate            }
3859310e4285b3fc951c3524d040726d1161015562cChristopher Tate            mFullBackupOutputStream = new BufferedOutputStream(tarstream);
3869310e4285b3fc951c3524d040726d1161015562cChristopher Tate        }
3879310e4285b3fc951c3524d040726d1161015562cChristopher Tate
3889310e4285b3fc951c3524d040726d1161015562cChristopher Tate        int bytesLeft = numBytes;
3899310e4285b3fc951c3524d040726d1161015562cChristopher Tate        while (bytesLeft > 0) {
3909ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            try {
3919310e4285b3fc951c3524d040726d1161015562cChristopher Tate            int nRead = mSocketInputStream.read(mFullBackupBuffer, 0, bytesLeft);
3929ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            if (nRead < 0) {
3939ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                // Something went wrong if we expect data but saw EOD
3949ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                Log.w(TAG, "Unexpected EOD; failing backup");
3955a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                return TRANSPORT_ERROR;
3969ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            }
3979ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            mFullBackupOutputStream.write(mFullBackupBuffer, 0, nRead);
3989310e4285b3fc951c3524d040726d1161015562cChristopher Tate            bytesLeft -= nRead;
3999ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            } catch (IOException e) {
4009ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                Log.e(TAG, "Error handling backup data for " + mFullTargetPackage);
4015a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                return TRANSPORT_ERROR;
4029ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            }
4039ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
40477a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate        if (DEBUG) {
40577a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate            Log.v(TAG, "   stored " + numBytes + " of data");
40677a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate        }
4075a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
4089ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
4099ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
410e079264b981d87c648921185528b553a86ae353dChristopher Tate    // For now we can't roll back, so just tear everything down.
411e079264b981d87c648921185528b553a86ae353dChristopher Tate    @Override
412e079264b981d87c648921185528b553a86ae353dChristopher Tate    public void cancelFullBackup() {
413e079264b981d87c648921185528b553a86ae353dChristopher Tate        if (DEBUG) {
414e079264b981d87c648921185528b553a86ae353dChristopher Tate            Log.i(TAG, "Canceling full backup of " + mFullTargetPackage);
415e079264b981d87c648921185528b553a86ae353dChristopher Tate        }
416e079264b981d87c648921185528b553a86ae353dChristopher Tate        File archive = tarballFile(mFullTargetPackage);
417e079264b981d87c648921185528b553a86ae353dChristopher Tate        tearDownFullBackup();
418e079264b981d87c648921185528b553a86ae353dChristopher Tate        if (archive.exists()) {
419e079264b981d87c648921185528b553a86ae353dChristopher Tate            archive.delete();
420e079264b981d87c648921185528b553a86ae353dChristopher Tate        }
421e079264b981d87c648921185528b553a86ae353dChristopher Tate    }
422e079264b981d87c648921185528b553a86ae353dChristopher Tate
4239ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    // ------------------------------------------------------------------------------------
4249bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    // Restore handling
42551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 };
4265a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
4275a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
42874318c98a0f67e042815798f85c75eb7f14390e1Christopher Tate    public RestoreSet[] getAvailableRestoreSets() {
429adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        long[] existing = new long[POSSIBLE_SETS.length + 1];
430adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        int num = 0;
431adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
4329ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // see which possible non-current sets exist...
433adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        for (long token : POSSIBLE_SETS) {
434adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            if ((new File(mDataDir, Long.toString(token))).exists()) {
435adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate                existing[num++] = token;
436adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            }
437adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
4389ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // ...and always the currently-active set last
439adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        existing[num++] = CURRENT_SET_TOKEN;
440adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
441adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        RestoreSet[] available = new RestoreSet[num];
442adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        for (int i = 0; i < available.length; i++) {
443adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            available[i] = new RestoreSet("Local disk image", "flash", existing[i]);
444adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
445adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        return available;
4469bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
4479bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
4485a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
44950c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    public long getCurrentRestoreSet() {
450adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // The current restore set always has the same token
451adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        return CURRENT_SET_TOKEN;
45250c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    }
45350c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate
4545a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
4550144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int startRestore(long token, PackageInfo[] packages) {
45651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        if (DEBUG) Log.v(TAG, "start restore " + token + " : " + packages.length
45751fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                + " matching packages");
458efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        mRestorePackages = packages;
459efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        mRestorePackage = -1;
4606a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        mRestoreSetDir = new File(mDataDir, Long.toString(token));
4616a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        mRestoreSetIncrementalDir = new File(mRestoreSetDir, INCREMENTAL_DIR);
4626a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        mRestoreSetFullDir = new File(mRestoreSetDir, FULL_DATA_DIR);
4635a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
464efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    }
4659bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
4666a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    @Override
4676a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    public RestoreDescription nextRestorePackage() {
46877a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate        if (DEBUG) {
46977a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate            Log.v(TAG, "nextRestorePackage() : mRestorePackage=" + mRestorePackage
47077a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                    + " length=" + mRestorePackages.length);
47177a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate        }
472efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
4736a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
4746a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        boolean found = false;
475efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        while (++mRestorePackage < mRestorePackages.length) {
476efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            String name = mRestorePackages[mRestorePackage].packageName;
4776a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
4786a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            // If we have key/value data for this package, deliver that
479a9b91864a1aedd71eaaaa43ee078cf93922289f3Christopher Tate            // skip packages where we have a data dir but no actual contents
4806a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            String[] contents = (new File(mRestoreSetIncrementalDir, name)).list();
481a9b91864a1aedd71eaaaa43ee078cf93922289f3Christopher Tate            if (contents != null && contents.length > 0) {
48277a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                if (DEBUG) {
48377a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                    Log.v(TAG, "  nextRestorePackage(TYPE_KEY_VALUE) @ "
48477a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                        + mRestorePackage + " = " + name);
48577a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                }
4866a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                mRestoreType = RestoreDescription.TYPE_KEY_VALUE;
4876a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                found = true;
4886a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            }
4896a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
4906a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            if (!found) {
4916a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                // No key/value data; check for [non-empty] full data
4926a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                File maybeFullData = new File(mRestoreSetFullDir, name);
4936a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                if (maybeFullData.length() > 0) {
49477a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                    if (DEBUG) {
49577a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                        Log.v(TAG, "  nextRestorePackage(TYPE_FULL_STREAM) @ "
49677a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                                + mRestorePackage + " = " + name);
49777a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                    }
4986a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                    mRestoreType = RestoreDescription.TYPE_FULL_STREAM;
4995a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                    mCurFullRestoreStream = null;   // ensure starting from the ground state
5006a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                    found = true;
5016a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                }
5026a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            }
5036a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
5046a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            if (found) {
5056a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                return new RestoreDescription(name, mRestoreType);
5062fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
50777a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate
50877a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate            if (DEBUG) {
50977a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                Log.v(TAG, "  ... package @ " + mRestorePackage + " = " + name
51077a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                        + " has no data; skipping");
51177a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate            }
5129bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        }
5139bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
514efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "  no more packages to restore");
5156a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        return RestoreDescription.NO_MORE_PACKAGES;
5169bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
5179bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
5185a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
5190144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int getRestoreData(ParcelFileDescriptor outFd) {
520efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
521efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
5226a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        if (mRestoreType != RestoreDescription.TYPE_KEY_VALUE) {
5236a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            throw new IllegalStateException("getRestoreData(fd) for non-key/value dataset");
5246a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        }
52551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        File packageDir = new File(mRestoreSetIncrementalDir,
52651fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                mRestorePackages[mRestorePackage].packageName);
5279bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
5282fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // The restore set is the concatenation of the individual record blobs,
529adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // each of which is a file in the package's directory.  We return the
530adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // data in lexical order sorted by key, so that apps which use synthetic
531adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // keys like BLOB_1, BLOB_2, etc will see the date in the most obvious
532adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // order.
533adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        ArrayList<DecodedFilename> blobs = contentsByKey(packageDir);
5340144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        if (blobs == null) {  // nextRestorePackage() ensures the dir exists, so this is an error
535adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            Log.e(TAG, "No keys for package: " + packageDir);
5365a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
537efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        }
538efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor
539efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        // We expect at least some data if the directory exists in the first place
540adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        if (DEBUG) Log.v(TAG, "  getRestoreData() found " + blobs.size() + " key files");
541efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
542efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        try {
543adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            for (DecodedFilename keyEntry : blobs) {
544adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate                File f = keyEntry.file;
545efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                FileInputStream in = new FileInputStream(f);
546efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                try {
547efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    int size = (int) f.length();
548efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    byte[] buf = new byte[size];
549efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    in.read(buf);
550adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate                    if (DEBUG) Log.v(TAG, "    ... key=" + keyEntry.key + " size=" + size);
551adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate                    out.writeEntityHeader(keyEntry.key, size);
552efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    out.writeEntityData(buf, size);
553efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                } finally {
554efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    in.close();
5558e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate                }
5562fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
5575a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_OK;
558efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        } catch (IOException e) {
559efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            Log.e(TAG, "Unable to read backup records", e);
5605a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
5612fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        }
5629bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
5633a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate
564adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    static class DecodedFilename implements Comparable<DecodedFilename> {
565adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        public File file;
566adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        public String key;
567adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
568adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        public DecodedFilename(File f) {
569adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            file = f;
570adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            key = new String(Base64.decode(f.getName()));
571adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
572adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
573adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        @Override
574adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        public int compareTo(DecodedFilename other) {
575adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            // sorts into ascending lexical order by decoded key
576adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            return key.compareTo(other.key);
577adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
578adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    }
579adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
580adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    // Return a list of the files in the given directory, sorted lexically by
581adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    // the Base64-decoded file name, not by the on-disk filename
582adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    private ArrayList<DecodedFilename> contentsByKey(File dir) {
583adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        File[] allFiles = dir.listFiles();
584adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        if (allFiles == null || allFiles.length == 0) {
585adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            return null;
586adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
587adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
588adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // Decode the filenames into keys then sort lexically by key
589adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        ArrayList<DecodedFilename> contents = new ArrayList<DecodedFilename>();
590adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        for (File f : allFiles) {
591adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            contents.add(new DecodedFilename(f));
592adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
593adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        Collections.sort(contents);
594adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        return contents;
595adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    }
596adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
5975a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
598efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    public void finishRestore() {
599efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "finishRestore()");
6005a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        if (mRestoreType == RestoreDescription.TYPE_FULL_STREAM) {
6015a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            resetFullRestoreState();
6025a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
6035a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mRestoreType = 0;
6043a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate    }
6059ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
6069ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    // ------------------------------------------------------------------------------------
6079ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    // Full restore handling
6089ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
6095a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    private void resetFullRestoreState() {
610824392bdbdd0d593c4bd2d26f197bb0009ff75c9Christopher Tate        IoUtils.closeQuietly(mCurFullRestoreStream);
6115a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mCurFullRestoreStream = null;
6125a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mFullRestoreSocketStream = null;
6135a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mFullRestoreBuffer = null;
6149ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
6159ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
6169ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    /**
6179ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * Ask the transport to provide data for the "current" package being restored.  The
6189ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * transport then writes some data to the socket supplied to this call, and returns
6199ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * the number of bytes written.  The system will then read that many bytes and
6209ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * stream them to the application's agent for restore, then will call this method again
6219ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * to receive the next chunk of the archive.  This sequence will be repeated until the
6229ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * transport returns zero indicating that all of the package's data has been delivered
6239ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * (or returns a negative value indicating some sort of hard error condition at the
6249ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * transport level).
6259ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *
6269ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * <p>After this method returns zero, the system will then call
6279ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * {@link #getNextFullRestorePackage()} to begin the restore process for the next
6289ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * application, and the sequence begins again.
6299ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *
6309ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * @param socket The file descriptor that the transport will use for delivering the
6319ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    streamed archive.
6329ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * @return 0 when no more data for the current package is available.  A positive value
6339ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    indicates the presence of that much data to be delivered to the app.  A negative
6349ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR},
6359ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    indicating a fatal error condition that precludes further restore operations
6369ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    on the current dataset.
6379ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     */
6385a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
6399ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
6405a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        if (mRestoreType != RestoreDescription.TYPE_FULL_STREAM) {
6415a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            throw new IllegalStateException("Asked for full restore data for non-stream package");
6425a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
6435a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
6445a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        // first chunk?
6455a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        if (mCurFullRestoreStream == null) {
6465a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            final String name = mRestorePackages[mRestorePackage].packageName;
6475a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            if (DEBUG) Log.i(TAG, "Starting full restore of " + name);
6485a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            File dataset = new File(mRestoreSetFullDir, name);
6495a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            try {
6505a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                mCurFullRestoreStream = new FileInputStream(dataset);
6515a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            } catch (IOException e) {
6525a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // If we can't open the target package's tarball, we return the single-package
6535a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // error code and let the caller go on to the next package.
6545a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                Log.e(TAG, "Unable to read archive for " + name);
6555a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                return TRANSPORT_PACKAGE_REJECTED;
6565a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            }
6575a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            mFullRestoreSocketStream = new FileOutputStream(socket.getFileDescriptor());
65889101f7fe89134a609459e80f779c1a5114e562aChristopher Tate            mFullRestoreBuffer = new byte[2*1024];
6595a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
6605a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
6615a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        int nRead;
6625a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        try {
6635a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            nRead = mCurFullRestoreStream.read(mFullRestoreBuffer);
6645a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            if (nRead < 0) {
6655a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // EOF: tell the caller we're done
6665a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                nRead = NO_MORE_DATA;
6675a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            } else if (nRead == 0) {
6685a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // This shouldn't happen when reading a FileInputStream; we should always
6695a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // get either a positive nonzero byte count or -1.  Log the situation and
6705a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // treat it as EOF.
6715a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                Log.w(TAG, "read() of archive file returned 0; treating as EOF");
6725a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                nRead = NO_MORE_DATA;
6735a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            } else {
6745a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                if (DEBUG) {
6755a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                    Log.i(TAG, "   delivering restore chunk: " + nRead);
6765a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                }
6775a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                mFullRestoreSocketStream.write(mFullRestoreBuffer, 0, nRead);
6785a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            }
6795a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        } catch (IOException e) {
6805a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;  // Hard error accessing the file; shouldn't happen
6815a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        } finally {
6825a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            // Most transports will need to explicitly close 'socket' here, but this transport
6835a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            // is in the same process as the caller so it can leave it up to the backup manager
6845a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            // to manage both socket fds.
6855a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
6865a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
6875a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return nRead;
6889ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
6895a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
6905a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    /**
6915a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM}
6925a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * data for restore, it will invoke this method to tell the transport that it should
6935a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * abandon the data download for the current package.  The OS will then either call
6945a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * {@link #nextRestorePackage()} again to move on to restoring the next package in the
6955a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * set being iterated over, or will call {@link #finishRestore()} to shut down the restore
6965a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * operation.
6975a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     *
6985a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the
6995a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     *    current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious
7005a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     *    transport-level failure.  If the transport reports an error here, the entire restore
7015a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     *    operation will immediately be finished with no further attempts to restore app data.
7025a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     */
7035a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
7045a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    public int abortFullRestore() {
7055a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        if (mRestoreType != RestoreDescription.TYPE_FULL_STREAM) {
7065a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            throw new IllegalStateException("abortFullRestore() but not currently restoring");
7075a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
7085a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        resetFullRestoreState();
7095a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mRestoreType = 0;
7105a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
7115a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    }
7125a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
713872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov    @Override
714872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov    public long getBackupQuota(String packageName, boolean isFullBackup) {
715872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        return isFullBackup ? FULL_BACKUP_SIZE_QUOTA : Long.MAX_VALUE;
716872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov    }
7179bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate}
718