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