14a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate/* 24a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * Copyright (C) 2011 The Android Open Source Project 34a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * 44a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * Licensed under the Apache License, Version 2.0 (the "License"); 54a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * you may not use this file except in compliance with the License. 64a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * You may obtain a copy of the License at 74a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * 84a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * http://www.apache.org/licenses/LICENSE-2.0 94a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * 104a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * Unless required by applicable law or agreed to in writing, software 114a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * distributed under the License is distributed on an "AS IS" BASIS, 124a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * See the License for the specific language governing permissions and 144a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * limitations under the License. 154a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate */ 164a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate 174a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tatepackage android.app.backup; 184a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate 19303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williamsimport android.content.Context; 20303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williamsimport android.content.pm.PackageManager; 21303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williamsimport android.content.res.XmlResourceParser; 228a372a0a280127743ce9a7ce4b6198c7a02d2a4fJeff Sharkeyimport android.os.ParcelFileDescriptor; 23303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williamsimport android.os.Process; 2460af594c3e5834fb710032c2294e36784cdd506fChristopher Tateimport android.os.storage.StorageManager; 2560af594c3e5834fb710032c2294e36784cdd506fChristopher Tateimport android.os.storage.StorageVolume; 2634385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.ErrnoException; 2734385d352da19805ae948215e2edbeedd16b7941Elliott Hughesimport android.system.Os; 28303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williamsimport android.text.TextUtils; 29303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williamsimport android.util.ArrayMap; 30303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williamsimport android.util.ArraySet; 3175a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tateimport android.util.Log; 3275a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate 33303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williamsimport com.android.internal.annotations.VisibleForTesting; 34303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 35303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williamsimport org.xmlpull.v1.XmlPullParser; 368a372a0a280127743ce9a7ce4b6198c7a02d2a4fJeff Sharkeyimport org.xmlpull.v1.XmlPullParserException; 37303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 3875a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tateimport java.io.File; 3975a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tateimport java.io.FileInputStream; 4075a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tateimport java.io.FileOutputStream; 4175a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tateimport java.io.IOException; 42303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williamsimport java.util.Map; 43303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williamsimport java.util.Set; 4475a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate 454a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate/** 464a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * Global constant definitions et cetera related to the full-backup-to-fd 4779ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * binary format. Nothing in this namespace is part of any API; it's all 4879ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * hidden details of the current implementation gathered into one location. 494a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * 504a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate * @hide 514a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate */ 524a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tatepublic class FullBackup { 5375a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate static final String TAG = "FullBackup"; 54303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams /** Enable this log tag to get verbose information while parsing the client xml. */ 55303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams static final String TAG_XML_PARSER = "BackupXmlParserLogging"; 5675a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate 5775a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate public static final String APK_TREE_TOKEN = "a"; 5875a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate public static final String OBB_TREE_TOKEN = "obb"; 592c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey 6075a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate public static final String ROOT_TREE_TOKEN = "r"; 612c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey public static final String FILES_TREE_TOKEN = "f"; 62a7835b6b6b00923b608a6bc3194e7840f67de7a8Christopher Tate public static final String NO_BACKUP_TREE_TOKEN = "nb"; 6375a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate public static final String DATABASE_TREE_TOKEN = "db"; 6475a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate public static final String SHAREDPREFS_TREE_TOKEN = "sp"; 6575a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate public static final String CACHE_TREE_TOKEN = "c"; 662c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey 672c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey public static final String DEVICE_ROOT_TREE_TOKEN = "d_r"; 682c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey public static final String DEVICE_FILES_TREE_TOKEN = "d_f"; 692c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey public static final String DEVICE_NO_BACKUP_TREE_TOKEN = "d_nb"; 702c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey public static final String DEVICE_DATABASE_TREE_TOKEN = "d_db"; 712c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey public static final String DEVICE_SHAREDPREFS_TREE_TOKEN = "d_sp"; 722c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey public static final String DEVICE_CACHE_TREE_TOKEN = "d_c"; 732c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey 742c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey public static final String MANAGED_EXTERNAL_TREE_TOKEN = "ef"; 7575a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate public static final String SHARED_STORAGE_TOKEN = "shared"; 7675a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate 7775a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate public static final String APPS_PREFIX = "apps/"; 78b0628bfd5aac480a0d412ac96b8af1d97ac01c30Christopher Tate public static final String SHARED_PREFIX = SHARED_STORAGE_TOKEN + "/"; 7975a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate 8075a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate public static final String FULL_BACKUP_INTENT_ACTION = "fullback"; 8175a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate public static final String FULL_RESTORE_INTENT_ACTION = "fullrest"; 8275a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate public static final String CONF_TOKEN_INTENT_EXTRA = "conftoken"; 8375a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate 8479ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate /** 8579ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * @hide 8679ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate */ 874a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate static public native int backupToTar(String packageName, String domain, 8811ae768cf1b8348e761ad9c09e98788da1e591b1Christopher Tate String linkdomain, String rootpath, String path, FullBackupDataOutput output); 8975a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate 90303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams private static final Map<String, BackupScheme> kPackageBackupSchemeMap = 91303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams new ArrayMap<String, BackupScheme>(); 92303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 93303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams static synchronized BackupScheme getBackupScheme(Context context) { 94303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams BackupScheme backupSchemeForPackage = 95303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams kPackageBackupSchemeMap.get(context.getPackageName()); 96303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (backupSchemeForPackage == null) { 97303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams backupSchemeForPackage = new BackupScheme(context); 98303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams kPackageBackupSchemeMap.put(context.getPackageName(), backupSchemeForPackage); 99303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 100303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return backupSchemeForPackage; 101303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 102303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 103303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams public static BackupScheme getBackupSchemeForTest(Context context) { 104303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams BackupScheme testing = new BackupScheme(context); 105303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams testing.mExcludes = new ArraySet(); 106303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams testing.mIncludes = new ArrayMap(); 107303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return testing; 108303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 109303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 110303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 11179ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate /** 11279ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * Copy data from a socket to the given File location on permanent storage. The 113f6d6fa8cbc0251da1900e858bb0379cda5014b6fChristopher Tate * modification time and access mode of the resulting file will be set if desired, 114f6d6fa8cbc0251da1900e858bb0379cda5014b6fChristopher Tate * although group/all rwx modes will be stripped: the restored file will not be 115f6d6fa8cbc0251da1900e858bb0379cda5014b6fChristopher Tate * accessible from outside the target application even if the original file was. 11679ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * If the {@code type} parameter indicates that the result should be a directory, 11779ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * the socket parameter may be {@code null}; even if it is valid, no data will be 11879ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * read from it in this case. 11979ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * <p> 12079ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * If the {@code mode} argument is negative, then the resulting output file will not 12179ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * have its access mode or last modification time reset as part of this operation. 12279ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * 12379ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * @param data Socket supplying the data to be copied to the output file. If the 12479ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * output is a directory, this may be {@code null}. 12579ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * @param size Number of bytes of data to copy from the socket to the file. At least 12679ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * this much data must be available through the {@code data} parameter. 12779ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * @param type Must be either {@link BackupAgent#TYPE_FILE} for ordinary file data 12879ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * or {@link BackupAgent#TYPE_DIRECTORY} for a directory. 12979ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * @param mode Unix-style file mode (as used by the chmod(2) syscall) to be set on 130f6d6fa8cbc0251da1900e858bb0379cda5014b6fChristopher Tate * the output file or directory. group/all rwx modes are stripped even if set 131f6d6fa8cbc0251da1900e858bb0379cda5014b6fChristopher Tate * in this parameter. If this parameter is negative then neither 132f6d6fa8cbc0251da1900e858bb0379cda5014b6fChristopher Tate * the mode nor the mtime values will be applied to the restored file. 13379ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * @param mtime A timestamp in the standard Unix epoch that will be imposed as the 13479ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * last modification time of the output file. if the {@code mode} parameter is 13579ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * negative then this parameter will be ignored. 13679ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * @param outFile Location within the filesystem to place the data. This must point 13746cc43c6fa7623820d4ae9149496cf96bb15f8a3Christopher Tate * to a location that is writeable by the caller, preferably using an absolute path. 13879ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate * @throws IOException 13979ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate */ 14079ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate static public void restoreFile(ParcelFileDescriptor data, 14179ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate long size, int type, long mode, long mtime, File outFile) throws IOException { 14279ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate if (type == BackupAgent.TYPE_DIRECTORY) { 14375a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate // Canonically a directory has no associated content, so we don't need to read 14475a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate // anything from the pipe in this case. Just create the directory here and 14575a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate // drop down to the final metadata adjustment. 14675a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate if (outFile != null) outFile.mkdirs(); 14775a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } else { 14875a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate FileOutputStream out = null; 14975a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate 15075a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate // Pull the data from the pipe, copying it to the output file, until we're done 15175a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate try { 15275a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate if (outFile != null) { 15375a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate File parent = outFile.getParentFile(); 15475a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate if (!parent.exists()) { 15575a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate // in practice this will only be for the default semantic directories, 15675a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate // and using the default mode for those is appropriate. 157303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // This can also happen for the case where a parent directory has been 158303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // excluded, but a file within that directory has been included. 15975a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate parent.mkdirs(); 16075a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } 16175a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate out = new FileOutputStream(outFile); 16275a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } 16375a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } catch (IOException e) { 16475a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate Log.e(TAG, "Unable to create/open file " + outFile.getPath(), e); 16575a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } 16675a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate 16775a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate byte[] buffer = new byte[32 * 1024]; 16875a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate final long origSize = size; 16975a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate FileInputStream in = new FileInputStream(data.getFileDescriptor()); 17075a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate while (size > 0) { 17175a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate int toRead = (size > buffer.length) ? buffer.length : (int)size; 17275a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate int got = in.read(buffer, 0, toRead); 17375a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate if (got <= 0) { 17475a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate Log.w(TAG, "Incomplete read: expected " + size + " but got " 17575a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate + (origSize - size)); 17675a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate break; 17775a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } 17875a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate if (out != null) { 17975a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate try { 18075a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate out.write(buffer, 0, got); 18175a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } catch (IOException e) { 18275a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate // Problem writing to the file. Quit copying data and delete 18375a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate // the file, but of course keep consuming the input stream. 18475a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate Log.e(TAG, "Unable to write to file " + outFile.getPath(), e); 18575a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate out.close(); 18675a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate out = null; 18775a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate outFile.delete(); 18875a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } 18975a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } 19075a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate size -= got; 19175a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } 19275a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate if (out != null) out.close(); 19375a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } 19475a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate 19575a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate // Now twiddle the state to match the backup, assuming all went well 19679ec80db70d788f35aa13346e4684ecbd401bd84Christopher Tate if (mode >= 0 && outFile != null) { 19775a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate try { 198f6d6fa8cbc0251da1900e858bb0379cda5014b6fChristopher Tate // explicitly prevent emplacement of files accessible by outside apps 199f6d6fa8cbc0251da1900e858bb0379cda5014b6fChristopher Tate mode &= 0700; 20034385d352da19805ae948215e2edbeedd16b7941Elliott Hughes Os.chmod(outFile.getPath(), (int)mode); 20175a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } catch (ErrnoException e) { 20275a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate e.rethrowAsIOException(); 20375a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } 20475a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate outFile.setLastModified(mtime); 20575a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } 20675a99709accef8cf221fd436d646727e7c8dd1f1Christopher Tate } 207303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 208303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams @VisibleForTesting 209303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams public static class BackupScheme { 210303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams private final File FILES_DIR; 211303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams private final File DATABASE_DIR; 212303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams private final File ROOT_DIR; 213303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams private final File SHAREDPREF_DIR; 214303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams private final File CACHE_DIR; 215303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams private final File NOBACKUP_DIR; 216303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 2172c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey private final File DEVICE_FILES_DIR; 2182c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey private final File DEVICE_DATABASE_DIR; 2192c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey private final File DEVICE_ROOT_DIR; 2202c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey private final File DEVICE_SHAREDPREF_DIR; 2212c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey private final File DEVICE_CACHE_DIR; 2222c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey private final File DEVICE_NOBACKUP_DIR; 2232c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey 2242c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey private final File EXTERNAL_DIR; 2252c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey 226303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams final int mFullBackupContent; 227303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams final PackageManager mPackageManager; 22860af594c3e5834fb710032c2294e36784cdd506fChristopher Tate final StorageManager mStorageManager; 229303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams final String mPackageName; 230303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 23160af594c3e5834fb710032c2294e36784cdd506fChristopher Tate // lazy initialized, only when needed 23260af594c3e5834fb710032c2294e36784cdd506fChristopher Tate private StorageVolume[] mVolumes = null; 23360af594c3e5834fb710032c2294e36784cdd506fChristopher Tate 234303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams /** 235303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * Parse out the semantic domains into the correct physical location. 236303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams */ 237303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams String tokenToDirectoryPath(String domainToken) { 238303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams try { 2392c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey if (domainToken.equals(FullBackup.FILES_TREE_TOKEN)) { 240303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return FILES_DIR.getCanonicalPath(); 241303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if (domainToken.equals(FullBackup.DATABASE_TREE_TOKEN)) { 242303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return DATABASE_DIR.getCanonicalPath(); 243303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if (domainToken.equals(FullBackup.ROOT_TREE_TOKEN)) { 244303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return ROOT_DIR.getCanonicalPath(); 245303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if (domainToken.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)) { 246303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return SHAREDPREF_DIR.getCanonicalPath(); 247303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if (domainToken.equals(FullBackup.CACHE_TREE_TOKEN)) { 248303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return CACHE_DIR.getCanonicalPath(); 2492c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if (domainToken.equals(FullBackup.NO_BACKUP_TREE_TOKEN)) { 2502c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return NOBACKUP_DIR.getCanonicalPath(); 2512c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if (domainToken.equals(FullBackup.DEVICE_FILES_TREE_TOKEN)) { 2522c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return DEVICE_FILES_DIR.getCanonicalPath(); 2532c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if (domainToken.equals(FullBackup.DEVICE_DATABASE_TREE_TOKEN)) { 2542c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return DEVICE_DATABASE_DIR.getCanonicalPath(); 2552c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if (domainToken.equals(FullBackup.DEVICE_ROOT_TREE_TOKEN)) { 2562c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return DEVICE_ROOT_DIR.getCanonicalPath(); 2572c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if (domainToken.equals(FullBackup.DEVICE_SHAREDPREFS_TREE_TOKEN)) { 2582c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return DEVICE_SHAREDPREF_DIR.getCanonicalPath(); 2592c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if (domainToken.equals(FullBackup.DEVICE_CACHE_TREE_TOKEN)) { 2602c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return DEVICE_CACHE_DIR.getCanonicalPath(); 2612c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if (domainToken.equals(FullBackup.DEVICE_NO_BACKUP_TREE_TOKEN)) { 2622c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return DEVICE_NOBACKUP_DIR.getCanonicalPath(); 263303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if (domainToken.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) { 264303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (EXTERNAL_DIR != null) { 265303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return EXTERNAL_DIR.getCanonicalPath(); 266303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else { 267303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return null; 268303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 26960af594c3e5834fb710032c2294e36784cdd506fChristopher Tate } else if (domainToken.startsWith(FullBackup.SHARED_PREFIX)) { 27060af594c3e5834fb710032c2294e36784cdd506fChristopher Tate return sharedDomainToPath(domainToken); 271303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 272303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // Not a supported location 273303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.i(TAG, "Unrecognized domain " + domainToken); 274303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return null; 27560af594c3e5834fb710032c2294e36784cdd506fChristopher Tate } catch (Exception e) { 276303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.i(TAG, "Error reading directory for domain: " + domainToken); 277303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return null; 278303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 279303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 280303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 28160af594c3e5834fb710032c2294e36784cdd506fChristopher Tate 28260af594c3e5834fb710032c2294e36784cdd506fChristopher Tate private String sharedDomainToPath(String domain) throws IOException { 28360af594c3e5834fb710032c2294e36784cdd506fChristopher Tate // already known to start with SHARED_PREFIX, so we just look after that 28460af594c3e5834fb710032c2294e36784cdd506fChristopher Tate final String volume = domain.substring(FullBackup.SHARED_PREFIX.length()); 28560af594c3e5834fb710032c2294e36784cdd506fChristopher Tate final StorageVolume[] volumes = getVolumeList(); 28660af594c3e5834fb710032c2294e36784cdd506fChristopher Tate final int volNum = Integer.parseInt(volume); 28760af594c3e5834fb710032c2294e36784cdd506fChristopher Tate if (volNum < mVolumes.length) { 28860af594c3e5834fb710032c2294e36784cdd506fChristopher Tate return volumes[volNum].getPathFile().getCanonicalPath(); 28960af594c3e5834fb710032c2294e36784cdd506fChristopher Tate } 29060af594c3e5834fb710032c2294e36784cdd506fChristopher Tate return null; 29160af594c3e5834fb710032c2294e36784cdd506fChristopher Tate } 29260af594c3e5834fb710032c2294e36784cdd506fChristopher Tate 29360af594c3e5834fb710032c2294e36784cdd506fChristopher Tate private StorageVolume[] getVolumeList() { 29460af594c3e5834fb710032c2294e36784cdd506fChristopher Tate if (mStorageManager != null) { 29560af594c3e5834fb710032c2294e36784cdd506fChristopher Tate if (mVolumes == null) { 29660af594c3e5834fb710032c2294e36784cdd506fChristopher Tate mVolumes = mStorageManager.getVolumeList(); 29760af594c3e5834fb710032c2294e36784cdd506fChristopher Tate } 29860af594c3e5834fb710032c2294e36784cdd506fChristopher Tate } else { 29960af594c3e5834fb710032c2294e36784cdd506fChristopher Tate Log.e(TAG, "Unable to access Storage Manager"); 30060af594c3e5834fb710032c2294e36784cdd506fChristopher Tate } 30160af594c3e5834fb710032c2294e36784cdd506fChristopher Tate return mVolumes; 30260af594c3e5834fb710032c2294e36784cdd506fChristopher Tate } 30360af594c3e5834fb710032c2294e36784cdd506fChristopher Tate 304303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams /** 305303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * A map of domain -> list of canonical file names in that domain that are to be included. 306303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * We keep track of the domain so that we can go through the file system in order later on. 307303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams */ 308303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Map<String, Set<String>> mIncludes; 309303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams /**e 310303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * List that will be populated with the canonical names of each file or directory that is 311303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * to be excluded. 312303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams */ 313303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams ArraySet<String> mExcludes; 314303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 315303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams BackupScheme(Context context) { 316303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams mFullBackupContent = context.getApplicationInfo().fullBackupContent; 31760af594c3e5834fb710032c2294e36784cdd506fChristopher Tate mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); 318303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams mPackageManager = context.getPackageManager(); 319303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams mPackageName = context.getPackageName(); 3202c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey 3212c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey // System apps have control over where their default storage context 3222c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey // is pointed, so we're always explicit when building paths. 3238a372a0a280127743ce9a7ce4b6198c7a02d2a4fJeff Sharkey final Context ceContext = context.createCredentialProtectedStorageContext(); 3242c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey FILES_DIR = ceContext.getFilesDir(); 3252c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey DATABASE_DIR = ceContext.getDatabasePath("foo").getParentFile(); 3262c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey ROOT_DIR = ceContext.getDataDir(); 3272c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey SHAREDPREF_DIR = ceContext.getSharedPreferencesPath("foo").getParentFile(); 3282c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey CACHE_DIR = ceContext.getCacheDir(); 3292c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey NOBACKUP_DIR = ceContext.getNoBackupFilesDir(); 3302c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey 3318a372a0a280127743ce9a7ce4b6198c7a02d2a4fJeff Sharkey final Context deContext = context.createDeviceProtectedStorageContext(); 3322c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey DEVICE_FILES_DIR = deContext.getFilesDir(); 3332c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey DEVICE_DATABASE_DIR = deContext.getDatabasePath("foo").getParentFile(); 3342c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey DEVICE_ROOT_DIR = deContext.getDataDir(); 3352c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey DEVICE_SHAREDPREF_DIR = deContext.getSharedPreferencesPath("foo").getParentFile(); 3362c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey DEVICE_CACHE_DIR = deContext.getCacheDir(); 3372c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey DEVICE_NOBACKUP_DIR = deContext.getNoBackupFilesDir(); 3382c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey 339303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (android.os.Process.myUid() != Process.SYSTEM_UID) { 340303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams EXTERNAL_DIR = context.getExternalFilesDir(null); 341303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else { 342303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams EXTERNAL_DIR = null; 343303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 344303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 345303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 346303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams boolean isFullBackupContentEnabled() { 347303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (mFullBackupContent < 0) { 348303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // android:fullBackupContent="false", bail. 349303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { 350303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(FullBackup.TAG_XML_PARSER, "android:fullBackupContent - \"false\""); 351303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 352303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return false; 353303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 354303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return true; 355303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 356303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 357303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams /** 358303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * @return A mapping of domain -> canonical paths within that domain. Each of these paths 359303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * specifies a file that the client has explicitly included in their backup set. If this 360303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * map is empty we will back up the entire data directory (including managed external 361303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * storage). 362303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams */ 363303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams public synchronized Map<String, Set<String>> maybeParseAndGetCanonicalIncludePaths() 364303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams throws IOException, XmlPullParserException { 365303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (mIncludes == null) { 366303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams maybeParseBackupSchemeLocked(); 367303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 368303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return mIncludes; 369303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 370303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 371303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams /** 372303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * @return A set of canonical paths that are to be excluded from the backup/restore set. 373303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams */ 374303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams public synchronized ArraySet<String> maybeParseAndGetCanonicalExcludePaths() 375303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams throws IOException, XmlPullParserException { 376303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (mExcludes == null) { 377303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams maybeParseBackupSchemeLocked(); 378303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 379303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return mExcludes; 380303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 381303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 382303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams private void maybeParseBackupSchemeLocked() throws IOException, XmlPullParserException { 383303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // This not being null is how we know that we've tried to parse the xml already. 384303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams mIncludes = new ArrayMap<String, Set<String>>(); 385303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams mExcludes = new ArraySet<String>(); 386303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 387303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (mFullBackupContent == 0) { 388303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // android:fullBackupContent="true" which means that we'll do everything. 389303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { 390303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(FullBackup.TAG_XML_PARSER, "android:fullBackupContent - \"true\""); 391303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 392303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else { 393303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // android:fullBackupContent="@xml/some_resource". 394303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) { 395303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(FullBackup.TAG_XML_PARSER, 396303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams "android:fullBackupContent - found xml resource"); 397303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 398303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams XmlResourceParser parser = null; 399303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams try { 400303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams parser = mPackageManager 401303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams .getResourcesForApplication(mPackageName) 402303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams .getXml(mFullBackupContent); 403303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams parseBackupSchemeFromXmlLocked(parser, mExcludes, mIncludes); 404303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } catch (PackageManager.NameNotFoundException e) { 405303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // Throw it as an IOException 406303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams throw new IOException(e); 407303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } finally { 408303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (parser != null) { 409303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams parser.close(); 410303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 411303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 412303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 413303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 414303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 415303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams @VisibleForTesting 416303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams public void parseBackupSchemeFromXmlLocked(XmlPullParser parser, 417303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Set<String> excludes, 418303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Map<String, Set<String>> includes) 419303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams throws IOException, XmlPullParserException { 420303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams int event = parser.getEventType(); // START_DOCUMENT 421303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams while (event != XmlPullParser.START_TAG) { 422303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams event = parser.next(); 423303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 424303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 425303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (!"full-backup-content".equals(parser.getName())) { 426303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams throw new XmlPullParserException("Xml file didn't start with correct tag" + 427303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams " (<full-backup-content>). Found \"" + parser.getName() + "\""); 428303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 429303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 430303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { 431303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "\n"); 432303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "===================================================="); 433303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "Found valid fullBackupContent; parsing xml resource."); 434303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "===================================================="); 435303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, ""); 436303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 437303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 438303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { 439303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams switch (event) { 440303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams case XmlPullParser.START_TAG: 441303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams validateInnerTagContents(parser); 442303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams final String domainFromXml = parser.getAttributeValue(null, "domain"); 443303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams final File domainDirectory = 444303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams getDirectoryForCriteriaDomain(domainFromXml); 445303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (domainDirectory == null) { 446303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { 447303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "...parsing \"" + parser.getName() + "\": " 448303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams + "domain=\"" + domainFromXml + "\" invalid; skipping"); 449303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 450303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams break; 451303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 452303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams final File canonicalFile = 453303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams extractCanonicalFile(domainDirectory, 454303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams parser.getAttributeValue(null, "path")); 455303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (canonicalFile == null) { 456303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams break; 457303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 458303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 459303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Set<String> activeSet = parseCurrentTagForDomain( 460303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams parser, excludes, includes, domainFromXml); 461303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams activeSet.add(canonicalFile.getCanonicalPath()); 462303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { 463303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "...parsed " + canonicalFile.getCanonicalPath() 464303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams + " for domain \"" + domainFromXml + "\""); 465303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 466303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 467303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // Special case journal files (not dirs) for sqlite database. frowny-face. 468303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // Note that for a restore, the file is never a directory (b/c it doesn't 469303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // exist). We have no way of knowing a priori whether or not to expect a 470303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // dir, so we add the -journal anyway to be safe. 471303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if ("database".equals(domainFromXml) && !canonicalFile.isDirectory()) { 472303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams final String canonicalJournalPath = 473303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams canonicalFile.getCanonicalPath() + "-journal"; 474303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams activeSet.add(canonicalJournalPath); 475303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { 476303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "...automatically generated " 47780e8db3d9cbdd6f42ca83b535abd26d761dc26eeDmitry Polukhin + canonicalJournalPath + ". Ignore if nonexistent."); 47880e8db3d9cbdd6f42ca83b535abd26d761dc26eeDmitry Polukhin } 4792c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey final String canonicalWalPath = 4802c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey canonicalFile.getCanonicalPath() + "-wal"; 4812c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey activeSet.add(canonicalWalPath); 4822c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { 4832c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey Log.v(TAG_XML_PARSER, "...automatically generated " 4842c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey + canonicalWalPath + ". Ignore if nonexistent."); 4852c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } 48680e8db3d9cbdd6f42ca83b535abd26d761dc26eeDmitry Polukhin } 48780e8db3d9cbdd6f42ca83b535abd26d761dc26eeDmitry Polukhin 48880e8db3d9cbdd6f42ca83b535abd26d761dc26eeDmitry Polukhin // Special case for sharedpref files (not dirs) also add ".xml" suffix file. 48980e8db3d9cbdd6f42ca83b535abd26d761dc26eeDmitry Polukhin if ("sharedpref".equals(domainFromXml) && !canonicalFile.isDirectory() && 49080e8db3d9cbdd6f42ca83b535abd26d761dc26eeDmitry Polukhin !canonicalFile.getCanonicalPath().endsWith(".xml")) { 49180e8db3d9cbdd6f42ca83b535abd26d761dc26eeDmitry Polukhin final String canonicalXmlPath = 49280e8db3d9cbdd6f42ca83b535abd26d761dc26eeDmitry Polukhin canonicalFile.getCanonicalPath() + ".xml"; 49380e8db3d9cbdd6f42ca83b535abd26d761dc26eeDmitry Polukhin activeSet.add(canonicalXmlPath); 49480e8db3d9cbdd6f42ca83b535abd26d761dc26eeDmitry Polukhin if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { 49580e8db3d9cbdd6f42ca83b535abd26d761dc26eeDmitry Polukhin Log.v(TAG_XML_PARSER, "...automatically generated " 49680e8db3d9cbdd6f42ca83b535abd26d761dc26eeDmitry Polukhin + canonicalXmlPath + ". Ignore if nonexistent."); 497303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 498303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 499303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 500303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 501303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { 502303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "\n"); 503303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "Xml resource parsing complete."); 504303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "Final tally."); 505303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "Includes:"); 506303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (includes.isEmpty()) { 507303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, " ...nothing specified (This means the entirety of app" 508303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams + " data minus excludes)"); 509303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else { 510303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams for (Map.Entry<String, Set<String>> entry : includes.entrySet()) { 511303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, " domain=" + entry.getKey()); 512303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams for (String includeData : entry.getValue()) { 513303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, " " + includeData); 514303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 515303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 516303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 517303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 518303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "Excludes:"); 519303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (excludes.isEmpty()) { 520303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, " ...nothing to exclude."); 521303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else { 522303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams for (String excludeData : excludes) { 523303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, " " + excludeData); 524303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 525303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 526303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 527303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, " "); 528303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "===================================================="); 529303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "\n"); 530303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 531303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 532303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 533303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams private Set<String> parseCurrentTagForDomain(XmlPullParser parser, 534303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Set<String> excludes, 535303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Map<String, Set<String>> includes, 536303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams String domain) 537303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams throws XmlPullParserException { 538303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if ("include".equals(parser.getName())) { 539303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams final String domainToken = getTokenForXmlDomain(domain); 540303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Set<String> includeSet = includes.get(domainToken); 541303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (includeSet == null) { 542303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams includeSet = new ArraySet<String>(); 543303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams includes.put(domainToken, includeSet); 544303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 545303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return includeSet; 546303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if ("exclude".equals(parser.getName())) { 547303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return excludes; 548303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else { 549303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // Unrecognised tag => hard failure. 550303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { 551303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "Invalid tag found in xml \"" 552303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams + parser.getName() + "\"; aborting operation."); 553303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 554303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams throw new XmlPullParserException("Unrecognised tag in backup" + 555303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams " criteria xml (" + parser.getName() + ")"); 556303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 557303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 558303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 559303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams /** 560303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * Map xml specified domain (human-readable, what clients put in their manifest's xml) to 561303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * BackupAgent internal data token. 562303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * @return null if the xml domain was invalid. 563303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams */ 564303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams private String getTokenForXmlDomain(String xmlDomain) { 565303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if ("root".equals(xmlDomain)) { 566303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return FullBackup.ROOT_TREE_TOKEN; 567303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if ("file".equals(xmlDomain)) { 5682c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return FullBackup.FILES_TREE_TOKEN; 569303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if ("database".equals(xmlDomain)) { 570303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return FullBackup.DATABASE_TREE_TOKEN; 571303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if ("sharedpref".equals(xmlDomain)) { 572303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return FullBackup.SHAREDPREFS_TREE_TOKEN; 5732c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if ("device_root".equals(xmlDomain)) { 5742c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return FullBackup.DEVICE_ROOT_TREE_TOKEN; 5752c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if ("device_file".equals(xmlDomain)) { 5762c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return FullBackup.DEVICE_FILES_TREE_TOKEN; 5772c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if ("device_database".equals(xmlDomain)) { 5782c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return FullBackup.DEVICE_DATABASE_TREE_TOKEN; 5792c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if ("device_sharedpref".equals(xmlDomain)) { 5802c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return FullBackup.DEVICE_SHAREDPREFS_TREE_TOKEN; 581303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if ("external".equals(xmlDomain)) { 582303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return FullBackup.MANAGED_EXTERNAL_TREE_TOKEN; 583303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else { 584303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return null; 585303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 586303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 587303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 588303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams /** 589303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * 590303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * @param domain Directory where the specified file should exist. Not null. 591303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * @param filePathFromXml parsed from xml. Not sanitised before calling this function so may be 592303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * null. 593303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * @return The canonical path of the file specified or null if no such file exists. 594303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams */ 595303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams private File extractCanonicalFile(File domain, String filePathFromXml) { 596303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (filePathFromXml == null) { 597303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams // Allow things like <include domain="sharedpref"/> 598303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams filePathFromXml = ""; 599303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 600303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (filePathFromXml.contains("..")) { 601303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { 602303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "...resolved \"" + domain.getPath() + " " + filePathFromXml 603303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams + "\", but the \"..\" path is not permitted; skipping."); 604303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 605303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return null; 606303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 607303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (filePathFromXml.contains("//")) { 608303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { 609303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams Log.v(TAG_XML_PARSER, "...resolved \"" + domain.getPath() + " " + filePathFromXml 610303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams + "\", which contains the invalid \"//\" sequence; skipping."); 611303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 612303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return null; 613303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 614303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return new File(domain, filePathFromXml); 615303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 616303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 617303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams /** 618303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * @param domain parsed from xml. Not sanitised before calling this function so may be null. 619303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * @return The directory relevant to the domain specified. 620303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams */ 621303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams private File getDirectoryForCriteriaDomain(String domain) { 622303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (TextUtils.isEmpty(domain)) { 623303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return null; 624303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 625303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if ("file".equals(domain)) { 626303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return FILES_DIR; 627303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if ("database".equals(domain)) { 628303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return DATABASE_DIR; 629303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if ("root".equals(domain)) { 630303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return ROOT_DIR; 631303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if ("sharedpref".equals(domain)) { 632303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return SHAREDPREF_DIR; 6332c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if ("device_file".equals(domain)) { 6342c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return DEVICE_FILES_DIR; 6352c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if ("device_database".equals(domain)) { 6362c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return DEVICE_DATABASE_DIR; 6372c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if ("device_root".equals(domain)) { 6382c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return DEVICE_ROOT_DIR; 6392c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey } else if ("device_sharedpref".equals(domain)) { 6402c1ba9a961d4f96c26df260ee437655ad9e7c03eJeff Sharkey return DEVICE_SHAREDPREF_DIR; 641303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else if ("external".equals(domain)) { 642303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return EXTERNAL_DIR; 643303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } else { 644303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams return null; 645303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 646303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 647303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams 648303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams /** 649303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * Let's be strict about the type of xml the client can write. If we see anything untoward, 650303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams * throw an XmlPullParserException. 651303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams */ 652303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams private void validateInnerTagContents(XmlPullParser parser) 653303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams throws XmlPullParserException { 654303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (parser.getAttributeCount() > 2) { 655303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams throw new XmlPullParserException("At most 2 tag attributes allowed for \"" 656303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams + parser.getName() + "\" tag (\"domain\" & \"path\"."); 657303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 658303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams if (!"include".equals(parser.getName()) && !"exclude".equals(parser.getName())) { 659303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams throw new XmlPullParserException("A valid tag is one of \"<include/>\" or" + 660303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams " \"<exclude/>. You provided \"" + parser.getName() + "\""); 661303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 662303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 663303650c9cdb7cec88e7ec20747b161d9fff10719Matthew Williams } 6644a627c71ff53a4fca1f961f4b1dcc0461df18a06Christopher Tate} 665