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
19eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufinoimport android.app.backup.BackupAgent;
204528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tateimport android.app.backup.BackupDataInput;
214528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tateimport android.app.backup.BackupDataOutput;
2274318c98a0f67e042815798f85c75eb7f14390e1Christopher Tateimport android.app.backup.BackupTransport;
236a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tateimport android.app.backup.RestoreDescription;
244528186e0d65fc68ef0dd1941aa2ac8aefcd55a3Christopher Tateimport android.app.backup.RestoreSet;
25cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tateimport android.content.ComponentName;
269bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.content.Context;
27a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tateimport android.content.Intent;
289bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.content.pm.PackageInfo;
299bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.os.Environment;
309bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.os.ParcelFileDescriptor;
31f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughesimport android.system.ErrnoException;
32f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughesimport android.system.Os;
33f97c63350abcc6715ba9fdc21fd3405d0f7ba716Elliott Hughesimport android.system.StructStat;
34c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tateimport android.util.ArrayMap;
359bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport android.util.Log;
369bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
374140faeebbfa23d56068c1862b2913fb62145f4fBrian Carlstromimport com.android.org.bouncycastle.util.encoders.Base64;
38e9190a2750e1fb67e300d2c128227cc9b7339efeChristopher Tate
39824392bdbdd0d593c4bd2d26f197bb0009ff75c9Christopher Tateimport libcore.io.IoUtils;
40824392bdbdd0d593c4bd2d26f197bb0009ff75c9Christopher Tate
419ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tateimport java.io.BufferedOutputStream;
429bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.File;
439bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileInputStream;
449ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tateimport java.io.FileNotFoundException;
459bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.FileOutputStream;
469bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tateimport java.io.IOException;
47adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tateimport java.util.ArrayList;
48adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tateimport java.util.Collections;
49b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate
509bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate/**
519bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate * Backup transport for stashing stuff into a known location on disk, and
529bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate * later restoring from there.  For testing only.
539bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate */
549bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
5574318c98a0f67e042815798f85c75eb7f14390e1Christopher Tatepublic class LocalTransport extends BackupTransport {
569bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    private static final String TAG = "LocalTransport";
57a50cd8d4264ca98e19b858596de3a223ba6bf42eEd Heyl    private static final boolean DEBUG = false;
589bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
595cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate    private static final String TRANSPORT_DIR_NAME
605cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate            = "com.android.internal.backup.LocalTransport";
615cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
62a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    private static final String TRANSPORT_DESTINATION_STRING
63a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate            = "Backing up to debug-only private cache";
64a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate
659679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    private static final String TRANSPORT_DATA_MANAGEMENT_LABEL
669679410db578e179c7559e7a52bb21c8082e9631Christopher Tate            = "";
679679410db578e179c7559e7a52bb21c8082e9631Christopher Tate
686a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private static final String INCREMENTAL_DIR = "_delta";
696a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    private static final String FULL_DATA_DIR = "_full";
706a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
71adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    // The currently-active restore set always has the same (nonzero!) token
72adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    private static final long CURRENT_SET_TOKEN = 1;
7350c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate
74c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate    // Size quotas at reasonable values, similar to the current cloud-storage limits
75872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov    private static final long FULL_BACKUP_SIZE_QUOTA = 25 * 1024 * 1024;
76b6e73c96705c83db1f45686f2cd735d06ceb468eShreyas Basarge    private static final long KEY_VALUE_BACKUP_SIZE_QUOTA = 5 * 1024 * 1024;
77b6e73c96705c83db1f45686f2cd735d06ceb468eShreyas Basarge
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;
909bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
919ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    // Additional bookkeeping for full backup
929ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private String mFullTargetPackage;
939ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private ParcelFileDescriptor mSocket;
949ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private FileInputStream mSocketInputStream;
959ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private BufferedOutputStream mFullBackupOutputStream;
969ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    private byte[] mFullBackupBuffer;
97872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov    private long mFullBackupSize;
989ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
995a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    private FileInputStream mCurFullRestoreStream;
1005a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    private FileOutputStream mFullRestoreSocketStream;
1015a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    private byte[] mFullRestoreBuffer;
102eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino    private final LocalTransportParameters mParameters;
1039bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
104de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate    private void makeDataDirs() {
105adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        mCurrentSetDir.mkdirs();
106de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate        mCurrentSetFullDir.mkdir();
107de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate        mCurrentSetIncrementalDir.mkdir();
108de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate    }
109de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate
110eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino    public LocalTransport(Context context, LocalTransportParameters parameters) {
111de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate        mContext = context;
112eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino        mParameters = parameters;
113de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate        makeDataDirs();
1149bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
1159bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
116eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino    LocalTransportParameters getParameters() {
117eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino        return mParameters;
118eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino    }
119eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino
1205a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
121cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tate    public String name() {
122cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tate        return new ComponentName(mContext, this.getClass()).flattenToShortString();
123cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tate    }
124cefba58d14f9669b57c16b4ea493779d882c43bdChristopher Tate
1255a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
126a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    public Intent configurationIntent() {
127a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        // The local transport is not user-configurable
128a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        return null;
129a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    }
130a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate
1315a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
132a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    public String currentDestinationString() {
133a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate        return TRANSPORT_DESTINATION_STRING;
1349679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    }
1359679410db578e179c7559e7a52bb21c8082e9631Christopher Tate
1369679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    public Intent dataManagementIntent() {
1379679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        // The local transport does not present a data-management UI
1389679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        // TODO: consider adding simple UI to wipe the archives entirely,
1399679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        // for cleaning up the cache partition.
1409679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        return null;
1419679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    }
1429679410db578e179c7559e7a52bb21c8082e9631Christopher Tate
1439679410db578e179c7559e7a52bb21c8082e9631Christopher Tate    public String dataManagementLabel() {
1449679410db578e179c7559e7a52bb21c8082e9631Christopher Tate        return TRANSPORT_DATA_MANAGEMENT_LABEL;
145a8ddef346cece1ad229e270ac4deebbd41ba6721Chris Tate    }
1465cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
1475a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
1480144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public String transportDirName() {
1495cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate        return TRANSPORT_DIR_NAME;
1505cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate    }
1515cb400bd72726c22f641f334951b35ce2ddcfeefChristopher Tate
1525a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
153eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino    public int getTransportFlags() {
154eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino        int flags = super.getTransportFlags();
155eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino        // Testing for a fake flag and having it set as a boolean in settings prevents anyone from
156eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino        // using this it to pull data from the agent
157eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino        if (mParameters.isFakeEncryptionFlag()) {
158eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino            flags |= BackupAgent.FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED;
159eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino        }
160eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino        return flags;
161eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino    }
162eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino
163eaa78b92a5ef191d8c581811b2d15920011e0e02Bernardo Rufino    @Override
1640144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public long requestBackupTime() {
1659bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        // any time is a good time for local backup
1669bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        return 0;
1679bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
1689bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
1695a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
1700144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int initializeDevice() {
1710144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        if (DEBUG) Log.v(TAG, "wiping all data");
172adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        deleteContents(mCurrentSetDir);
173de2826b1e95573cf1cf065f46d661ab27bade00cChristopher Tate        makeDataDirs();
1745a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
1750144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    }
1760144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor
177c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate    // Encapsulation of a single k/v element change
178c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate    private class KVOperation {
179c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        final String key;     // Element filename, not the raw key, for efficiency
180c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        final byte[] value;   // null when this is a deletion operation
181c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate
182c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        KVOperation(String k, byte[] v) {
183c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            key = k;
184c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            value = v;
185c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        }
186c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate    }
187c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate
1885a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
1890144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
190d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov        return performBackup(packageInfo, data, /*flags=*/ 0);
191d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov    }
192d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov
193d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov    @Override
194d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data, int flags) {
195d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov        boolean isIncremental = (flags & FLAG_INCREMENTAL) != 0;
196d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov        boolean isNonIncremental = (flags & FLAG_NON_INCREMENTAL) != 0;
197d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov
198d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov        if (isIncremental) {
199d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov            Log.i(TAG, "Performing incremental backup for " + packageInfo.packageName);
200d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov        } else if (isNonIncremental) {
201d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov            Log.i(TAG, "Performing non-incremental backup for " + packageInfo.packageName);
202d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov        } else {
203d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov            Log.i(TAG, "Performing backup for " + packageInfo.packageName);
204d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov        }
205d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov
206b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate        if (DEBUG) {
207b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate            try {
208d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov                StructStat ss = Os.fstat(data.getFileDescriptor());
209d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov                Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName
210d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov                        + " size=" + ss.st_size + " flags=" + flags);
211b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate            } catch (ErrnoException e) {
212b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                Log.w(TAG, "Unable to stat input file in performBackup() on "
213b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate                        + packageInfo.packageName);
214b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate            }
215b048c33d5bdaec747195dfedf971d4d9155f5000Christopher Tate        }
2169bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
2179ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        File packageDir = new File(mCurrentSetIncrementalDir, packageInfo.packageName);
218d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov        boolean hasDataForPackage = !packageDir.mkdirs();
219d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov
220d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov        if (isIncremental) {
221d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov            if (mParameters.isNonIncrementalOnly() || !hasDataForPackage) {
222d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov                if (mParameters.isNonIncrementalOnly()) {
223d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov                    Log.w(TAG, "Transport is in non-incremental only mode.");
224d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov
225d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov                } else {
226d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov                    Log.w(TAG,
227d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov                            "Requested incremental, but transport currently stores no data for the "
228d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov                                    + "package, requesting non-incremental retry.");
229d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov                }
230d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov                return TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED;
231d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov            }
232d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov        }
233d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov        if (isNonIncremental && hasDataForPackage) {
234d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov            Log.w(TAG, "Requested non-incremental, deleting existing data.");
235d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov            clearBackupData(packageInfo);
236d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov            packageDir.mkdirs();
237d903074ea80e70bb02a433ecbcef5b3d72a7c6c9Anton Philippov        }
2389bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
2392fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // Each 'record' in the restore set is kept in its own file, named by
2402fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // the record key.  Wind through the data file, extracting individual
241c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        // record operations and building a list of all the updates to apply
2422fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // in this update.
243c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        final ArrayList<KVOperation> changeOps;
2442fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        try {
245c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            changeOps = parseBackupStream(data);
2462fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        } catch (IOException e) {
2472fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            // oops, something went wrong.  abort the operation and return error.
248c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            Log.v(TAG, "Exception reading backup input", e);
2495a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
2502fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        }
251c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate
252c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        // Okay, now we've parsed out the delta's individual operations.  We need to measure
253c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        // the effect against what we already have in the datastore to detect quota overrun.
254c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        // So, we first need to tally up the current in-datastore size per key.
255c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        final ArrayMap<String, Integer> datastore = new ArrayMap<>();
256c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        int totalSize = parseKeySizes(packageDir, datastore);
257c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate
258c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        // ... and now figure out the datastore size that will result from applying the
259c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        // sequence of delta operations
260c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        if (DEBUG) {
261c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            if (changeOps.size() > 0) {
262c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                Log.v(TAG, "Calculating delta size impact");
263c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            } else {
264c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                Log.v(TAG, "No operations in backup stream, so no size change");
265c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            }
266c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        }
267c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        int updatedSize = totalSize;
268c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        for (KVOperation op : changeOps) {
269c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            // Deduct the size of the key we're about to replace, if any
270c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            final Integer curSize = datastore.get(op.key);
271c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            if (curSize != null) {
272c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                updatedSize -= curSize.intValue();
273c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                if (DEBUG && op.value == null) {
274c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                    Log.v(TAG, "  delete " + op.key + ", updated total " + updatedSize);
275c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                }
276c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            }
277c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate
278c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            // And add back the size of the value we're about to store, if any
279c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            if (op.value != null) {
280c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                updatedSize += op.value.length;
281c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                if (DEBUG) {
282c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                    Log.v(TAG, ((curSize == null) ? "  new " : "  replace ")
283c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                            +  op.key + ", updated total " + updatedSize);
284c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                }
285c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            }
286c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        }
287c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate
288c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        // If our final size is over quota, report the failure
289c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        if (updatedSize > KEY_VALUE_BACKUP_SIZE_QUOTA) {
290c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            if (DEBUG) {
291c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                Log.i(TAG, "New datastore size " + updatedSize
292c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                        + " exceeds quota " + KEY_VALUE_BACKUP_SIZE_QUOTA);
293c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            }
294c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            return TRANSPORT_QUOTA_EXCEEDED;
295c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        }
296c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate
297c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        // No problem with storage size, so go ahead and apply the delta operations
298c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        // (in the order that the app provided them)
299c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        for (KVOperation op : changeOps) {
300c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            File element = new File(packageDir, op.key);
301c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate
302c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            // this is either a deletion or a rewrite-from-zero, so we can just remove
303c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            // the existing file and proceed in either case.
304c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            element.delete();
305c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate
306c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            // if this wasn't a deletion, put the new data in place
307c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            if (op.value != null) {
308c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                try (FileOutputStream out = new FileOutputStream(element)) {
309c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                    out.write(op.value, 0, op.value.length);
310c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                } catch (IOException e) {
311c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                    Log.e(TAG, "Unable to update key file " + element);
312c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                    return TRANSPORT_ERROR;
313c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                }
314c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            }
315c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        }
316c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        return TRANSPORT_OK;
317c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate    }
318c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate
319c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate    // Parses a backup stream into individual key/value operations
320c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate    private ArrayList<KVOperation> parseBackupStream(ParcelFileDescriptor data)
321c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            throws IOException {
322c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        ArrayList<KVOperation> changeOps = new ArrayList<>();
323c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        BackupDataInput changeSet = new BackupDataInput(data.getFileDescriptor());
324c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        while (changeSet.readNextHeader()) {
325c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            String key = changeSet.getKey();
326c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            String base64Key = new String(Base64.encode(key.getBytes()));
327c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            int dataSize = changeSet.getDataSize();
328c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            if (DEBUG) {
329c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                Log.v(TAG, "  Delta operation key " + key + "   size " + dataSize
330c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                        + "   key64 " + base64Key);
331c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            }
332c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate
333c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            byte[] buf = (dataSize >= 0) ? new byte[dataSize] : null;
334c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            if (dataSize >= 0) {
335c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                changeSet.readEntityData(buf, 0, dataSize);
336c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            }
337c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            changeOps.add(new KVOperation(base64Key, buf));
338c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        }
339c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        return changeOps;
340c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate    }
341c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate
342c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate    // Reads the given datastore directory, building a table of the value size of each
343c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate    // keyed element, and returning the summed total.
344c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate    private int parseKeySizes(File packageDir, ArrayMap<String, Integer> datastore) {
345c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        int totalSize = 0;
346c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        final String[] elements = packageDir.list();
347c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        if (elements != null) {
348c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            if (DEBUG) {
349c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                Log.v(TAG, "Existing datastore contents:");
350c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            }
351c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            for (String file : elements) {
352c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                File element = new File(packageDir, file);
353c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                String key = file;  // filename
354c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                int size = (int) element.length();
355c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                totalSize += size;
356c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                if (DEBUG) {
357c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                    Log.v(TAG, "  key " + key + "   size " + size);
358c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                }
359c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                datastore.put(key, size);
360c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            }
361c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            if (DEBUG) {
362c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                Log.v(TAG, "  TOTAL: " + totalSize);
363c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            }
364c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        } else {
365c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            if (DEBUG) {
366c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate                Log.v(TAG, "No existing data for this package");
367c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate            }
368c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        }
369c8a9d421a56960843fab2a2bc4412c33c09ab1e9Christopher Tate        return totalSize;
370efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    }
371ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate
37225a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    // Deletes the contents but not the given directory
37325a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    private void deleteContents(File dirname) {
37425a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        File[] contents = dirname.listFiles();
37525a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        if (contents != null) {
37625a747f5c47f25c1a18961b03507f309b84924feChristopher Tate            for (File f : contents) {
37725a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                if (f.isDirectory()) {
37825a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    // delete the directory's contents then fall through
37925a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    // and delete the directory itself.
38025a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                    deleteContents(f);
38125a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                }
38225a747f5c47f25c1a18961b03507f309b84924feChristopher Tate                f.delete();
38325a747f5c47f25c1a18961b03507f309b84924feChristopher Tate            }
38425a747f5c47f25c1a18961b03507f309b84924feChristopher Tate        }
38525a747f5c47f25c1a18961b03507f309b84924feChristopher Tate    }
38625a747f5c47f25c1a18961b03507f309b84924feChristopher Tate
3875a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
3880144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int clearBackupData(PackageInfo packageInfo) {
389ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate        if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
390ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate
3919ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        File packageDir = new File(mCurrentSetIncrementalDir, packageInfo.packageName);
3920abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate        final File[] fileset = packageDir.listFiles();
3930abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate        if (fileset != null) {
3940abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            for (File f : fileset) {
3950abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate                f.delete();
3960abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            }
3970abf6a001461a4c2ea31ddc44a60b003b4e0554dChristopher Tate            packageDir.delete();
398ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate        }
3999ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
4009ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        packageDir = new File(mCurrentSetFullDir, packageInfo.packageName);
4019ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        final File[] tarballs = packageDir.listFiles();
4029ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (tarballs != null) {
4039ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            for (File f : tarballs) {
4049ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                f.delete();
4059ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            }
4069ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            packageDir.delete();
4079ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
4089ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
4095a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
410ee0e78af5af3bf23dd928fe5e0ebeb39157eaf66Christopher Tate    }
4119bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
4125a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
4130144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int finishBackup() {
414e079264b981d87c648921185528b553a86ae353dChristopher Tate        if (DEBUG) Log.v(TAG, "finishBackup() of " + mFullTargetPackage);
415e079264b981d87c648921185528b553a86ae353dChristopher Tate        return tearDownFullBackup();
416e079264b981d87c648921185528b553a86ae353dChristopher Tate    }
417e079264b981d87c648921185528b553a86ae353dChristopher Tate
418e079264b981d87c648921185528b553a86ae353dChristopher Tate    // ------------------------------------------------------------------------------------
419e079264b981d87c648921185528b553a86ae353dChristopher Tate    // Full backup handling
420e079264b981d87c648921185528b553a86ae353dChristopher Tate
421e079264b981d87c648921185528b553a86ae353dChristopher Tate    private int tearDownFullBackup() {
4229ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (mSocket != null) {
4239ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            try {
4249310e4285b3fc951c3524d040726d1161015562cChristopher Tate                if (mFullBackupOutputStream != null) {
4259310e4285b3fc951c3524d040726d1161015562cChristopher Tate                    mFullBackupOutputStream.flush();
4269310e4285b3fc951c3524d040726d1161015562cChristopher Tate                    mFullBackupOutputStream.close();
4279310e4285b3fc951c3524d040726d1161015562cChristopher Tate                }
4289ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                mSocketInputStream = null;
4299ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                mFullTargetPackage = null;
4309ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                mSocket.close();
4319ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            } catch (IOException e) {
43289101f7fe89134a609459e80f779c1a5114e562aChristopher Tate                if (DEBUG) {
433e079264b981d87c648921185528b553a86ae353dChristopher Tate                    Log.w(TAG, "Exception caught in tearDownFullBackup()", e);
43489101f7fe89134a609459e80f779c1a5114e562aChristopher Tate                }
4355a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                return TRANSPORT_ERROR;
4369ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            } finally {
4379ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                mSocket = null;
4389310e4285b3fc951c3524d040726d1161015562cChristopher Tate                mFullBackupOutputStream = null;
4399ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            }
4409ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
4415a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
4429bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
4439bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
444e079264b981d87c648921185528b553a86ae353dChristopher Tate    private File tarballFile(String pkgName) {
445e079264b981d87c648921185528b553a86ae353dChristopher Tate        return new File(mCurrentSetFullDir, pkgName);
446e079264b981d87c648921185528b553a86ae353dChristopher Tate    }
4475a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
4485a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
4499ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    public long requestFullBackupTime() {
4509ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        return 0;
4519ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
4529ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
4535a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
4549310e4285b3fc951c3524d040726d1161015562cChristopher Tate    public int checkFullBackupSize(long size) {
455872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        int result = TRANSPORT_OK;
4569310e4285b3fc951c3524d040726d1161015562cChristopher Tate        // Decline zero-size "backups"
457872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        if (size <= 0) {
458872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov            result = TRANSPORT_PACKAGE_REJECTED;
459872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        } else if (size > FULL_BACKUP_SIZE_QUOTA) {
460872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov            result = TRANSPORT_QUOTA_EXCEEDED;
461872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        }
4629310e4285b3fc951c3524d040726d1161015562cChristopher Tate        if (result != TRANSPORT_OK) {
4639310e4285b3fc951c3524d040726d1161015562cChristopher Tate            if (DEBUG) {
4649310e4285b3fc951c3524d040726d1161015562cChristopher Tate                Log.v(TAG, "Declining backup of size " + size);
4659310e4285b3fc951c3524d040726d1161015562cChristopher Tate            }
4669310e4285b3fc951c3524d040726d1161015562cChristopher Tate        }
4679310e4285b3fc951c3524d040726d1161015562cChristopher Tate        return result;
4689310e4285b3fc951c3524d040726d1161015562cChristopher Tate    }
4699310e4285b3fc951c3524d040726d1161015562cChristopher Tate
4709310e4285b3fc951c3524d040726d1161015562cChristopher Tate    @Override
4719ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) {
4729ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (mSocket != null) {
4739ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            Log.e(TAG, "Attempt to initiate full backup while one is in progress");
4745a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
4759ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
4769ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
4779ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (DEBUG) {
4789ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            Log.i(TAG, "performFullBackup : " + targetPackage);
4799ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
4809ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
4819ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // We know a priori that we run in the system process, so we need to make
4829ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // sure to dup() our own copy of the socket fd.  Transports which run in
4839ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // their own processes must not do this.
4849ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        try {
485872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov            mFullBackupSize = 0;
4869ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            mSocket = ParcelFileDescriptor.dup(socket.getFileDescriptor());
4879ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            mSocketInputStream = new FileInputStream(mSocket.getFileDescriptor());
4889ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        } catch (IOException e) {
4899ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            Log.e(TAG, "Unable to process socket for full backup");
4905a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
4919ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
4929ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
4939ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        mFullTargetPackage = targetPackage.packageName;
4949ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        mFullBackupBuffer = new byte[4096];
4959ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
4965a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
4979ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
4989ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
4995a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
5009310e4285b3fc951c3524d040726d1161015562cChristopher Tate    public int sendBackupData(final int numBytes) {
5019310e4285b3fc951c3524d040726d1161015562cChristopher Tate        if (mSocket == null) {
5029ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            Log.w(TAG, "Attempted sendBackupData before performFullBackup");
5035a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
5049ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
5059ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
506872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        mFullBackupSize += numBytes;
507872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        if (mFullBackupSize > FULL_BACKUP_SIZE_QUOTA) {
508872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov            return TRANSPORT_QUOTA_EXCEEDED;
509872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov        }
510872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov
5119ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        if (numBytes > mFullBackupBuffer.length) {
5129ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            mFullBackupBuffer = new byte[numBytes];
5139ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
5149310e4285b3fc951c3524d040726d1161015562cChristopher Tate
5159310e4285b3fc951c3524d040726d1161015562cChristopher Tate        if (mFullBackupOutputStream == null) {
5169310e4285b3fc951c3524d040726d1161015562cChristopher Tate            FileOutputStream tarstream;
5179310e4285b3fc951c3524d040726d1161015562cChristopher Tate            try {
5189310e4285b3fc951c3524d040726d1161015562cChristopher Tate                File tarball = tarballFile(mFullTargetPackage);
5199310e4285b3fc951c3524d040726d1161015562cChristopher Tate                tarstream = new FileOutputStream(tarball);
5209310e4285b3fc951c3524d040726d1161015562cChristopher Tate            } catch (FileNotFoundException e) {
5219310e4285b3fc951c3524d040726d1161015562cChristopher Tate                return TRANSPORT_ERROR;
5229310e4285b3fc951c3524d040726d1161015562cChristopher Tate            }
5239310e4285b3fc951c3524d040726d1161015562cChristopher Tate            mFullBackupOutputStream = new BufferedOutputStream(tarstream);
5249310e4285b3fc951c3524d040726d1161015562cChristopher Tate        }
5259310e4285b3fc951c3524d040726d1161015562cChristopher Tate
5269310e4285b3fc951c3524d040726d1161015562cChristopher Tate        int bytesLeft = numBytes;
5279310e4285b3fc951c3524d040726d1161015562cChristopher Tate        while (bytesLeft > 0) {
5289ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            try {
5299310e4285b3fc951c3524d040726d1161015562cChristopher Tate            int nRead = mSocketInputStream.read(mFullBackupBuffer, 0, bytesLeft);
5309ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            if (nRead < 0) {
5319ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                // Something went wrong if we expect data but saw EOD
5329ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                Log.w(TAG, "Unexpected EOD; failing backup");
5335a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                return TRANSPORT_ERROR;
5349ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            }
5359ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            mFullBackupOutputStream.write(mFullBackupBuffer, 0, nRead);
5369310e4285b3fc951c3524d040726d1161015562cChristopher Tate            bytesLeft -= nRead;
5379ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            } catch (IOException e) {
5389ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate                Log.e(TAG, "Error handling backup data for " + mFullTargetPackage);
5395a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                return TRANSPORT_ERROR;
5409ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate            }
5419ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        }
54277a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate        if (DEBUG) {
54377a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate            Log.v(TAG, "   stored " + numBytes + " of data");
54477a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate        }
5455a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
5469ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
5479ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
548e079264b981d87c648921185528b553a86ae353dChristopher Tate    // For now we can't roll back, so just tear everything down.
549e079264b981d87c648921185528b553a86ae353dChristopher Tate    @Override
550e079264b981d87c648921185528b553a86ae353dChristopher Tate    public void cancelFullBackup() {
551e079264b981d87c648921185528b553a86ae353dChristopher Tate        if (DEBUG) {
552e079264b981d87c648921185528b553a86ae353dChristopher Tate            Log.i(TAG, "Canceling full backup of " + mFullTargetPackage);
553e079264b981d87c648921185528b553a86ae353dChristopher Tate        }
554e079264b981d87c648921185528b553a86ae353dChristopher Tate        File archive = tarballFile(mFullTargetPackage);
555e079264b981d87c648921185528b553a86ae353dChristopher Tate        tearDownFullBackup();
556e079264b981d87c648921185528b553a86ae353dChristopher Tate        if (archive.exists()) {
557e079264b981d87c648921185528b553a86ae353dChristopher Tate            archive.delete();
558e079264b981d87c648921185528b553a86ae353dChristopher Tate        }
559e079264b981d87c648921185528b553a86ae353dChristopher Tate    }
560e079264b981d87c648921185528b553a86ae353dChristopher Tate
5619ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    // ------------------------------------------------------------------------------------
5629bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    // Restore handling
56351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate    static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 };
5645a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
5655a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
56674318c98a0f67e042815798f85c75eb7f14390e1Christopher Tate    public RestoreSet[] getAvailableRestoreSets() {
567adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        long[] existing = new long[POSSIBLE_SETS.length + 1];
568adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        int num = 0;
569adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
5709ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // see which possible non-current sets exist...
571adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        for (long token : POSSIBLE_SETS) {
572adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            if ((new File(mDataDir, Long.toString(token))).exists()) {
573adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate                existing[num++] = token;
574adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            }
575adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
5769ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate        // ...and always the currently-active set last
577adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        existing[num++] = CURRENT_SET_TOKEN;
578adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
579adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        RestoreSet[] available = new RestoreSet[num];
580adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        for (int i = 0; i < available.length; i++) {
581adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            available[i] = new RestoreSet("Local disk image", "flash", existing[i]);
582adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
583adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        return available;
5849bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
5859bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
5865a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
58750c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    public long getCurrentRestoreSet() {
588adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // The current restore set always has the same token
589adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        return CURRENT_SET_TOKEN;
59050c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate    }
59150c6df04cf17a99c3959c306a4e0e10da6d85c46Christopher Tate
5925a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
5930144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int startRestore(long token, PackageInfo[] packages) {
59451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        if (DEBUG) Log.v(TAG, "start restore " + token + " : " + packages.length
59551fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                + " matching packages");
596efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        mRestorePackages = packages;
597efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        mRestorePackage = -1;
5986a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        mRestoreSetDir = new File(mDataDir, Long.toString(token));
5996a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        mRestoreSetIncrementalDir = new File(mRestoreSetDir, INCREMENTAL_DIR);
6006a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        mRestoreSetFullDir = new File(mRestoreSetDir, FULL_DATA_DIR);
6015a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
602efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    }
6039bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
6046a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    @Override
6056a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate    public RestoreDescription nextRestorePackage() {
60677a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate        if (DEBUG) {
60777a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate            Log.v(TAG, "nextRestorePackage() : mRestorePackage=" + mRestorePackage
60877a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                    + " length=" + mRestorePackages.length);
60977a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate        }
610efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
6116a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
6126a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        boolean found = false;
613efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        while (++mRestorePackage < mRestorePackages.length) {
614efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            String name = mRestorePackages[mRestorePackage].packageName;
6156a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
6166a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            // If we have key/value data for this package, deliver that
617a9b91864a1aedd71eaaaa43ee078cf93922289f3Christopher Tate            // skip packages where we have a data dir but no actual contents
6186a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            String[] contents = (new File(mRestoreSetIncrementalDir, name)).list();
619a9b91864a1aedd71eaaaa43ee078cf93922289f3Christopher Tate            if (contents != null && contents.length > 0) {
62077a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                if (DEBUG) {
62177a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                    Log.v(TAG, "  nextRestorePackage(TYPE_KEY_VALUE) @ "
62277a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                        + mRestorePackage + " = " + name);
62377a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                }
6246a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                mRestoreType = RestoreDescription.TYPE_KEY_VALUE;
6256a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                found = true;
6266a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            }
6276a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
6286a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            if (!found) {
6296a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                // No key/value data; check for [non-empty] full data
6306a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                File maybeFullData = new File(mRestoreSetFullDir, name);
6316a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                if (maybeFullData.length() > 0) {
63277a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                    if (DEBUG) {
63377a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                        Log.v(TAG, "  nextRestorePackage(TYPE_FULL_STREAM) @ "
63477a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                                + mRestorePackage + " = " + name);
63577a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                    }
6366a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                    mRestoreType = RestoreDescription.TYPE_FULL_STREAM;
6375a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                    mCurFullRestoreStream = null;   // ensure starting from the ground state
6386a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                    found = true;
6396a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                }
6406a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            }
6416a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate
6426a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            if (found) {
6436a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate                return new RestoreDescription(name, mRestoreType);
6442fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
64577a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate
64677a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate            if (DEBUG) {
64777a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                Log.v(TAG, "  ... package @ " + mRestorePackage + " = " + name
64877a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate                        + " has no data; skipping");
64977a2d78dbf91d4799f811385b1e39ad89052e7ebChristopher Tate            }
6509bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate        }
6519bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
652efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "  no more packages to restore");
6536a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        return RestoreDescription.NO_MORE_PACKAGES;
6549bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
6559bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
6565a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
6570144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor    public int getRestoreData(ParcelFileDescriptor outFd) {
658efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
659efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
6606a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        if (mRestoreType != RestoreDescription.TYPE_KEY_VALUE) {
6616a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate            throw new IllegalStateException("getRestoreData(fd) for non-key/value dataset");
6626a49dd087f29cfca82d55dfabeb97439ef84b508Christopher Tate        }
66351fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate        File packageDir = new File(mRestoreSetIncrementalDir,
66451fea57e06fcb1dab1d239a5fff6e75ba2b7cee7Christopher Tate                mRestorePackages[mRestorePackage].packageName);
6659bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate
6662fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        // The restore set is the concatenation of the individual record blobs,
667adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // each of which is a file in the package's directory.  We return the
668adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // data in lexical order sorted by key, so that apps which use synthetic
669adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // keys like BLOB_1, BLOB_2, etc will see the date in the most obvious
670adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // order.
671adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        ArrayList<DecodedFilename> blobs = contentsByKey(packageDir);
6720144516e19b9fd5415a56f8b41191187e2344bb0Dan Egnor        if (blobs == null) {  // nextRestorePackage() ensures the dir exists, so this is an error
673adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            Log.e(TAG, "No keys for package: " + packageDir);
6745a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
675efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        }
676efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor
677efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        // We expect at least some data if the directory exists in the first place
678adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        if (DEBUG) Log.v(TAG, "  getRestoreData() found " + blobs.size() + " key files");
679efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
680efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        try {
681adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            for (DecodedFilename keyEntry : blobs) {
682adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate                File f = keyEntry.file;
683efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                FileInputStream in = new FileInputStream(f);
684efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                try {
685efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    int size = (int) f.length();
686efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    byte[] buf = new byte[size];
687efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    in.read(buf);
688adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate                    if (DEBUG) Log.v(TAG, "    ... key=" + keyEntry.key + " size=" + size);
689adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate                    out.writeEntityHeader(keyEntry.key, size);
690efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    out.writeEntityData(buf, size);
691efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                } finally {
692efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor                    in.close();
6938e55eac96d768a4de68a091f57487deadf6d0a87Christopher Tate                }
6942fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate            }
6955a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_OK;
696efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        } catch (IOException e) {
697efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor            Log.e(TAG, "Unable to read backup records", e);
6985a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;
6992fdd428e0f18384160f7c38ce3a2cd9ba7e7b2c2Christopher Tate        }
7009bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate    }
7013a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate
702adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    static class DecodedFilename implements Comparable<DecodedFilename> {
703adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        public File file;
704adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        public String key;
705adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
706adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        public DecodedFilename(File f) {
707adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            file = f;
708adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            key = new String(Base64.decode(f.getName()));
709adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
710adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
711adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        @Override
712adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        public int compareTo(DecodedFilename other) {
713adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            // sorts into ascending lexical order by decoded key
714adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            return key.compareTo(other.key);
715adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
716adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    }
717adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
718adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    // Return a list of the files in the given directory, sorted lexically by
719adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    // the Base64-decoded file name, not by the on-disk filename
720adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    private ArrayList<DecodedFilename> contentsByKey(File dir) {
721adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        File[] allFiles = dir.listFiles();
722adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        if (allFiles == null || allFiles.length == 0) {
723adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            return null;
724adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
725adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
726adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        // Decode the filenames into keys then sort lexically by key
727adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        ArrayList<DecodedFilename> contents = new ArrayList<DecodedFilename>();
728adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        for (File f : allFiles) {
729adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate            contents.add(new DecodedFilename(f));
730adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        }
731adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        Collections.sort(contents);
732adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate        return contents;
733adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate    }
734adfe8b86e9178a553b6db9722340fa4ff5201cf1Christopher Tate
7355a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
736efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor    public void finishRestore() {
737efe52647f6b41993be43a5f47d1178bb0468cec8Dan Egnor        if (DEBUG) Log.v(TAG, "finishRestore()");
7385a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        if (mRestoreType == RestoreDescription.TYPE_FULL_STREAM) {
7395a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            resetFullRestoreState();
7405a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
7415a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mRestoreType = 0;
7423a31a93b8a195ae2d0180e6dfbf292da2e581f50Christopher Tate    }
7439ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
7449ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    // ------------------------------------------------------------------------------------
7459ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    // Full restore handling
7469ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
7475a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    private void resetFullRestoreState() {
748824392bdbdd0d593c4bd2d26f197bb0009ff75c9Christopher Tate        IoUtils.closeQuietly(mCurFullRestoreStream);
7495a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mCurFullRestoreStream = null;
7505a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mFullRestoreSocketStream = null;
7515a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mFullRestoreBuffer = null;
7529ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
7539ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate
7549ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    /**
7559ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * Ask the transport to provide data for the "current" package being restored.  The
7569ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * transport then writes some data to the socket supplied to this call, and returns
7579ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * the number of bytes written.  The system will then read that many bytes and
7589ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * stream them to the application's agent for restore, then will call this method again
7599ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * to receive the next chunk of the archive.  This sequence will be repeated until the
7609ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * transport returns zero indicating that all of the package's data has been delivered
7619ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * (or returns a negative value indicating some sort of hard error condition at the
7629ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * transport level).
7639ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *
7649ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * <p>After this method returns zero, the system will then call
7659ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * {@link #getNextFullRestorePackage()} to begin the restore process for the next
7669ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * application, and the sequence begins again.
7679ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *
7689ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * @param socket The file descriptor that the transport will use for delivering the
7699ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    streamed archive.
7709ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     * @return 0 when no more data for the current package is available.  A positive value
7719ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    indicates the presence of that much data to be delivered to the app.  A negative
7729ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR},
7739ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    indicating a fatal error condition that precludes further restore operations
7749ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     *    on the current dataset.
7759ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate     */
7765a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
7779ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
7785a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        if (mRestoreType != RestoreDescription.TYPE_FULL_STREAM) {
7795a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            throw new IllegalStateException("Asked for full restore data for non-stream package");
7805a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
7815a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
7825a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        // first chunk?
7835a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        if (mCurFullRestoreStream == null) {
7845a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            final String name = mRestorePackages[mRestorePackage].packageName;
7855a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            if (DEBUG) Log.i(TAG, "Starting full restore of " + name);
7865a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            File dataset = new File(mRestoreSetFullDir, name);
7875a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            try {
7885a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                mCurFullRestoreStream = new FileInputStream(dataset);
7895a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            } catch (IOException e) {
7905a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // If we can't open the target package's tarball, we return the single-package
7915a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // error code and let the caller go on to the next package.
7925a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                Log.e(TAG, "Unable to read archive for " + name);
7935a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                return TRANSPORT_PACKAGE_REJECTED;
7945a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            }
7955a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            mFullRestoreSocketStream = new FileOutputStream(socket.getFileDescriptor());
79689101f7fe89134a609459e80f779c1a5114e562aChristopher Tate            mFullRestoreBuffer = new byte[2*1024];
7975a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
7985a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
7995a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        int nRead;
8005a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        try {
8015a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            nRead = mCurFullRestoreStream.read(mFullRestoreBuffer);
8025a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            if (nRead < 0) {
8035a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // EOF: tell the caller we're done
8045a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                nRead = NO_MORE_DATA;
8055a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            } else if (nRead == 0) {
8065a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // This shouldn't happen when reading a FileInputStream; we should always
8075a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // get either a positive nonzero byte count or -1.  Log the situation and
8085a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                // treat it as EOF.
8095a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                Log.w(TAG, "read() of archive file returned 0; treating as EOF");
8105a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                nRead = NO_MORE_DATA;
8115a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            } else {
8125a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                if (DEBUG) {
8135a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                    Log.i(TAG, "   delivering restore chunk: " + nRead);
8145a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                }
8155a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate                mFullRestoreSocketStream.write(mFullRestoreBuffer, 0, nRead);
8165a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            }
8175a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        } catch (IOException e) {
8185a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            return TRANSPORT_ERROR;  // Hard error accessing the file; shouldn't happen
8195a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        } finally {
8205a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            // Most transports will need to explicitly close 'socket' here, but this transport
8215a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            // is in the same process as the caller so it can leave it up to the backup manager
8225a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            // to manage both socket fds.
8235a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
8245a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
8255a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return nRead;
8269ff53a7100b1a40f5d2df3eb19a2f3f2fff39a46Christopher Tate    }
8275a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
8285a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    /**
8295a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM}
8305a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * data for restore, it will invoke this method to tell the transport that it should
8315a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * abandon the data download for the current package.  The OS will then either call
8325a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * {@link #nextRestorePackage()} again to move on to restoring the next package in the
8335a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * set being iterated over, or will call {@link #finishRestore()} to shut down the restore
8345a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * operation.
8355a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     *
8365a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the
8375a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     *    current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious
8385a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     *    transport-level failure.  If the transport reports an error here, the entire restore
8395a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     *    operation will immediately be finished with no further attempts to restore app data.
8405a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate     */
8415a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    @Override
8425a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    public int abortFullRestore() {
8435a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        if (mRestoreType != RestoreDescription.TYPE_FULL_STREAM) {
8445a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate            throw new IllegalStateException("abortFullRestore() but not currently restoring");
8455a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        }
8465a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        resetFullRestoreState();
8475a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        mRestoreType = 0;
8485a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate        return TRANSPORT_OK;
8495a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate    }
8505a009f9008d1f18b156c142b69e173109f5e218bChristopher Tate
851872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov    @Override
852872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov    public long getBackupQuota(String packageName, boolean isFullBackup) {
853b6e73c96705c83db1f45686f2cd735d06ceb468eShreyas Basarge        return isFullBackup ? FULL_BACKUP_SIZE_QUOTA : KEY_VALUE_BACKUP_SIZE_QUOTA;
854872d3b6e19933af6fa9ae65214b9f6df04fc3222Sergey Poromov    }
8559bbc21a773cbdfbef2876a75c32bda5839647751Christopher Tate}
856