diff options
| author | 2016-02-18 19:26:34 +0000 | |
|---|---|---|
| committer | 2016-02-18 19:26:36 +0000 | |
| commit | 003ee198518490c9572e610ddc21e8b3a024364c (patch) | |
| tree | abb5678eb8ca311529260505947e93dad8e0b51e | |
| parent | 4e334cfcd754dc081793b3c6b9aef46a04e926b5 (diff) | |
| parent | 2c1ba9a961d4f96c26df260ee437655ad9e7c03e (diff) | |
Merge "Make BackupManager encryption aware." into nyc-dev
| -rw-r--r-- | api/current.txt | 3 | ||||
| -rw-r--r-- | api/system-current.txt | 3 | ||||
| -rw-r--r-- | api/test-current.txt | 3 | ||||
| -rw-r--r-- | core/java/android/app/ContextImpl.java | 17 | ||||
| -rw-r--r-- | core/java/android/app/backup/BackupAgent.java | 152 | ||||
| -rw-r--r-- | core/java/android/app/backup/FullBackup.java | 91 | ||||
| -rw-r--r-- | core/java/android/content/Context.java | 19 | ||||
| -rw-r--r-- | core/java/android/content/ContextWrapper.java | 5 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/app/backup/FullBackupTest.java | 33 | ||||
| -rw-r--r-- | services/backup/java/com/android/server/backup/BackupManagerService.java | 15 | ||||
| -rw-r--r-- | test-runner/src/android/test/mock/MockContext.java | 5 | ||||
| -rw-r--r-- | tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java | 6 |
12 files changed, 279 insertions, 73 deletions
diff --git a/api/current.txt b/api/current.txt index 6110d1c584fe..1ebec4e56d4d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8006,6 +8006,7 @@ package android.content { method public final int getColor(int); method public final android.content.res.ColorStateList getColorStateList(int); method public abstract android.content.ContentResolver getContentResolver(); + method public abstract java.io.File getDataDir(); method public abstract java.io.File getDatabasePath(java.lang.String); method public abstract java.io.File getDir(java.lang.String, int); method public final android.graphics.drawable.Drawable getDrawable(int); @@ -8198,6 +8199,7 @@ package android.content { method public java.lang.ClassLoader getClassLoader(); method public java.io.File getCodeCacheDir(); method public android.content.ContentResolver getContentResolver(); + method public java.io.File getDataDir(); method public java.io.File getDatabasePath(java.lang.String); method public java.io.File getDir(java.lang.String, int); method public java.io.File getExternalCacheDir(); @@ -37632,6 +37634,7 @@ package android.test.mock { method public java.lang.ClassLoader getClassLoader(); method public java.io.File getCodeCacheDir(); method public android.content.ContentResolver getContentResolver(); + method public java.io.File getDataDir(); method public java.io.File getDatabasePath(java.lang.String); method public java.io.File getDir(java.lang.String, int); method public java.io.File getExternalCacheDir(); diff --git a/api/system-current.txt b/api/system-current.txt index c01f1c32d646..55df364e4da9 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -8297,6 +8297,7 @@ package android.content { method public final int getColor(int); method public final android.content.res.ColorStateList getColorStateList(int); method public abstract android.content.ContentResolver getContentResolver(); + method public abstract java.io.File getDataDir(); method public abstract java.io.File getDatabasePath(java.lang.String); method public abstract java.io.File getDir(java.lang.String, int); method public final android.graphics.drawable.Drawable getDrawable(int); @@ -8499,6 +8500,7 @@ package android.content { method public java.lang.ClassLoader getClassLoader(); method public java.io.File getCodeCacheDir(); method public android.content.ContentResolver getContentResolver(); + method public java.io.File getDataDir(); method public java.io.File getDatabasePath(java.lang.String); method public java.io.File getDir(java.lang.String, int); method public java.io.File getExternalCacheDir(); @@ -40380,6 +40382,7 @@ package android.test.mock { method public java.lang.ClassLoader getClassLoader(); method public java.io.File getCodeCacheDir(); method public android.content.ContentResolver getContentResolver(); + method public java.io.File getDataDir(); method public java.io.File getDatabasePath(java.lang.String); method public java.io.File getDir(java.lang.String, int); method public java.io.File getExternalCacheDir(); diff --git a/api/test-current.txt b/api/test-current.txt index 96d29d1f9c48..8fcb9bdc72ee 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -8009,6 +8009,7 @@ package android.content { method public final int getColor(int); method public final android.content.res.ColorStateList getColorStateList(int); method public abstract android.content.ContentResolver getContentResolver(); + method public abstract java.io.File getDataDir(); method public abstract java.io.File getDatabasePath(java.lang.String); method public abstract java.io.File getDir(java.lang.String, int); method public final android.graphics.drawable.Drawable getDrawable(int); @@ -8202,6 +8203,7 @@ package android.content { method public java.lang.ClassLoader getClassLoader(); method public java.io.File getCodeCacheDir(); method public android.content.ContentResolver getContentResolver(); + method public java.io.File getDataDir(); method public java.io.File getDatabasePath(java.lang.String); method public java.io.File getDir(java.lang.String, int); method public java.io.File getExternalCacheDir(); @@ -37647,6 +37649,7 @@ package android.test.mock { method public java.lang.ClassLoader getClassLoader(); method public java.io.File getCodeCacheDir(); method public android.content.ContentResolver getContentResolver(); + method public java.io.File getDataDir(); method public java.io.File getDatabasePath(java.lang.String); method public java.io.File getDir(java.lang.String, int); method public java.io.File getExternalCacheDir(); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 5e8d1908eb18..8884949f361c 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -460,7 +460,7 @@ class ContextImpl extends Context { private File getPreferencesDir() { synchronized (mSync) { if (mPreferencesDir == null) { - mPreferencesDir = new File(getDataDirFile(), "shared_prefs"); + mPreferencesDir = new File(getDataDir(), "shared_prefs"); } return ensurePrivateDirExists(mPreferencesDir); } @@ -525,7 +525,7 @@ class ContextImpl extends Context { public File getFilesDir() { synchronized (mSync) { if (mFilesDir == null) { - mFilesDir = new File(getDataDirFile(), "files"); + mFilesDir = new File(getDataDir(), "files"); } return ensurePrivateDirExists(mFilesDir); } @@ -535,7 +535,7 @@ class ContextImpl extends Context { public File getNoBackupFilesDir() { synchronized (mSync) { if (mNoBackupFilesDir == null) { - mNoBackupFilesDir = new File(getDataDirFile(), "no_backup"); + mNoBackupFilesDir = new File(getDataDir(), "no_backup"); } return ensurePrivateDirExists(mNoBackupFilesDir); } @@ -587,7 +587,7 @@ class ContextImpl extends Context { public File getCacheDir() { synchronized (mSync) { if (mCacheDir == null) { - mCacheDir = new File(getDataDirFile(), "cache"); + mCacheDir = new File(getDataDir(), "cache"); } return ensurePrivateDirExists(mCacheDir); } @@ -597,7 +597,7 @@ class ContextImpl extends Context { public File getCodeCacheDir() { synchronized (mSync) { if (mCodeCacheDir == null) { - mCodeCacheDir = new File(getDataDirFile(), "code_cache"); + mCodeCacheDir = new File(getDataDir(), "code_cache"); } return ensurePrivateDirExists(mCodeCacheDir); } @@ -724,7 +724,7 @@ class ContextImpl extends Context { if ("android".equals(getPackageName())) { mDatabasesDir = new File("/data/system"); } else { - mDatabasesDir = new File(getDataDirFile(), "databases"); + mDatabasesDir = new File(getDataDir(), "databases"); } } return ensurePrivateDirExists(mDatabasesDir); @@ -1920,7 +1920,8 @@ class ContextImpl extends Context { return mDisplayAdjustments; } - private File getDataDirFile() { + @Override + public File getDataDir() { if (mPackageInfo != null) { File res = null; if (isCredentialEncryptedStorage()) { @@ -1947,7 +1948,7 @@ class ContextImpl extends Context { public File getDir(String name, int mode) { checkMode(mode); name = "app_" + name; - File file = makeFilename(getDataDirFile(), name); + File file = makeFilename(getDataDir(), name); if (!file.exists()) { file.mkdir(); setFilePermissionsFromMode(file.getPath(), mode, diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 63f142552c28..aeb315611686 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -308,14 +308,31 @@ public abstract class BackupAgent extends ContextWrapper { final String packageName = getPackageName(); final ApplicationInfo appInfo = getApplicationInfo(); - String rootDir = new File(appInfo.dataDir).getCanonicalPath(); - String filesDir = getFilesDir().getCanonicalPath(); - String nobackupDir = getNoBackupFilesDir().getCanonicalPath(); - String databaseDir = getDatabasePath("foo").getParentFile().getCanonicalPath(); - String sharedPrefsDir = getSharedPrefsFile("foo").getParentFile().getCanonicalPath(); - String cacheDir = getCacheDir().getCanonicalPath(); - String codeCacheDir = getCodeCacheDir().getCanonicalPath(); - String libDir = (appInfo.nativeLibraryDir != null) + // System apps have control over where their default storage context + // is pointed, so we're always explicit when building paths. + final Context ceContext = createCredentialEncryptedStorageContext(); + final String rootDir = ceContext.getDataDir().getCanonicalPath(); + final String filesDir = ceContext.getFilesDir().getCanonicalPath(); + final String noBackupDir = ceContext.getNoBackupFilesDir().getCanonicalPath(); + final String databaseDir = ceContext.getDatabasePath("foo").getParentFile() + .getCanonicalPath(); + final String sharedPrefsDir = ceContext.getSharedPreferencesPath("foo").getParentFile() + .getCanonicalPath(); + final String cacheDir = ceContext.getCacheDir().getCanonicalPath(); + final String codeCacheDir = ceContext.getCodeCacheDir().getCanonicalPath(); + + final Context deContext = createDeviceEncryptedStorageContext(); + final String deviceRootDir = deContext.getDataDir().getCanonicalPath(); + final String deviceFilesDir = deContext.getFilesDir().getCanonicalPath(); + final String deviceNoBackupDir = deContext.getNoBackupFilesDir().getCanonicalPath(); + final String deviceDatabaseDir = deContext.getDatabasePath("foo").getParentFile() + .getCanonicalPath(); + final String deviceSharedPrefsDir = deContext.getSharedPreferencesPath("foo") + .getParentFile().getCanonicalPath(); + final String deviceCacheDir = deContext.getCacheDir().getCanonicalPath(); + final String deviceCodeCacheDir = deContext.getCodeCacheDir().getCanonicalPath(); + + final String libDir = (appInfo.nativeLibraryDir != null) ? new File(appInfo.nativeLibraryDir).getCanonicalPath() : null; @@ -325,30 +342,48 @@ public abstract class BackupAgent extends ContextWrapper { final ArraySet<String> traversalExcludeSet = new ArraySet<String>(); // Add the directories we always exclude. + traversalExcludeSet.add(filesDir); + traversalExcludeSet.add(noBackupDir); + traversalExcludeSet.add(databaseDir); + traversalExcludeSet.add(sharedPrefsDir); traversalExcludeSet.add(cacheDir); traversalExcludeSet.add(codeCacheDir); - traversalExcludeSet.add(nobackupDir); + + traversalExcludeSet.add(deviceFilesDir); + traversalExcludeSet.add(deviceNoBackupDir); + traversalExcludeSet.add(deviceDatabaseDir); + traversalExcludeSet.add(deviceSharedPrefsDir); + traversalExcludeSet.add(deviceCacheDir); + traversalExcludeSet.add(deviceCodeCacheDir); + if (libDir != null) { traversalExcludeSet.add(libDir); } - traversalExcludeSet.add(databaseDir); - traversalExcludeSet.add(sharedPrefsDir); - traversalExcludeSet.add(filesDir); - // Root dir first. applyXmlFiltersAndDoFullBackupForDomain( packageName, FullBackup.ROOT_TREE_TOKEN, manifestIncludeMap, manifestExcludeSet, traversalExcludeSet, data); traversalExcludeSet.add(rootDir); + applyXmlFiltersAndDoFullBackupForDomain( + packageName, FullBackup.DEVICE_ROOT_TREE_TOKEN, manifestIncludeMap, + manifestExcludeSet, traversalExcludeSet, data); + traversalExcludeSet.add(deviceRootDir); + // Data dir next. traversalExcludeSet.remove(filesDir); applyXmlFiltersAndDoFullBackupForDomain( - packageName, FullBackup.DATA_TREE_TOKEN, manifestIncludeMap, + packageName, FullBackup.FILES_TREE_TOKEN, manifestIncludeMap, manifestExcludeSet, traversalExcludeSet, data); traversalExcludeSet.add(filesDir); + traversalExcludeSet.remove(deviceFilesDir); + applyXmlFiltersAndDoFullBackupForDomain( + packageName, FullBackup.DEVICE_FILES_TREE_TOKEN, manifestIncludeMap, + manifestExcludeSet, traversalExcludeSet, data); + traversalExcludeSet.add(deviceFilesDir); + // Database directory. traversalExcludeSet.remove(databaseDir); applyXmlFiltersAndDoFullBackupForDomain( @@ -356,6 +391,12 @@ public abstract class BackupAgent extends ContextWrapper { manifestExcludeSet, traversalExcludeSet, data); traversalExcludeSet.add(databaseDir); + traversalExcludeSet.remove(deviceDatabaseDir); + applyXmlFiltersAndDoFullBackupForDomain( + packageName, FullBackup.DEVICE_DATABASE_TREE_TOKEN, manifestIncludeMap, + manifestExcludeSet, traversalExcludeSet, data); + traversalExcludeSet.add(deviceDatabaseDir); + // SharedPrefs. traversalExcludeSet.remove(sharedPrefsDir); applyXmlFiltersAndDoFullBackupForDomain( @@ -363,6 +404,12 @@ public abstract class BackupAgent extends ContextWrapper { manifestExcludeSet, traversalExcludeSet, data); traversalExcludeSet.add(sharedPrefsDir); + traversalExcludeSet.remove(deviceSharedPrefsDir); + applyXmlFiltersAndDoFullBackupForDomain( + packageName, FullBackup.DEVICE_SHAREDPREFS_TREE_TOKEN, manifestIncludeMap, + manifestExcludeSet, traversalExcludeSet, data); + traversalExcludeSet.add(deviceSharedPrefsDir); + // getExternalFilesDir() location associated with this app. Technically there should // not be any files here if the app does not properly have permission to access // external storage, but edge cases happen. fullBackupFileTree() catches @@ -445,27 +492,49 @@ public abstract class BackupAgent extends ContextWrapper { */ public final void fullBackupFile(File file, FullBackupDataOutput output) { // Look up where all of our various well-defined dir trees live on this device - String mainDir; - String filesDir; - String nbFilesDir; - String dbDir; - String spDir; - String cacheDir; - String codeCacheDir; - String libDir; + final String rootDir; + final String filesDir; + final String nbFilesDir; + final String dbDir; + final String spDir; + final String cacheDir; + final String codeCacheDir; + final String deviceRootDir; + final String deviceFilesDir; + final String deviceNbFilesDir; + final String deviceDbDir; + final String deviceSpDir; + final String deviceCacheDir; + final String deviceCodeCacheDir; + final String libDir; + String efDir = null; String filePath; ApplicationInfo appInfo = getApplicationInfo(); try { - mainDir = new File(appInfo.dataDir).getCanonicalPath(); - filesDir = getFilesDir().getCanonicalPath(); - nbFilesDir = getNoBackupFilesDir().getCanonicalPath(); - dbDir = getDatabasePath("foo").getParentFile().getCanonicalPath(); - spDir = getSharedPrefsFile("foo").getParentFile().getCanonicalPath(); - cacheDir = getCacheDir().getCanonicalPath(); - codeCacheDir = getCodeCacheDir().getCanonicalPath(); + // System apps have control over where their default storage context + // is pointed, so we're always explicit when building paths. + final Context ceContext = createCredentialEncryptedStorageContext(); + rootDir = ceContext.getDataDir().getCanonicalPath(); + filesDir = ceContext.getFilesDir().getCanonicalPath(); + nbFilesDir = ceContext.getNoBackupFilesDir().getCanonicalPath(); + dbDir = ceContext.getDatabasePath("foo").getParentFile().getCanonicalPath(); + spDir = ceContext.getSharedPreferencesPath("foo").getParentFile().getCanonicalPath(); + cacheDir = ceContext.getCacheDir().getCanonicalPath(); + codeCacheDir = ceContext.getCodeCacheDir().getCanonicalPath(); + + final Context deContext = createDeviceEncryptedStorageContext(); + deviceRootDir = deContext.getDataDir().getCanonicalPath(); + deviceFilesDir = deContext.getFilesDir().getCanonicalPath(); + deviceNbFilesDir = deContext.getNoBackupFilesDir().getCanonicalPath(); + deviceDbDir = deContext.getDatabasePath("foo").getParentFile().getCanonicalPath(); + deviceSpDir = deContext.getSharedPreferencesPath("foo").getParentFile() + .getCanonicalPath(); + deviceCacheDir = deContext.getCacheDir().getCanonicalPath(); + deviceCodeCacheDir = deContext.getCodeCacheDir().getCanonicalPath(); + libDir = (appInfo.nativeLibraryDir == null) ? null : new File(appInfo.nativeLibraryDir).getCanonicalPath(); @@ -489,8 +558,11 @@ public abstract class BackupAgent extends ContextWrapper { if (filePath.startsWith(cacheDir) || filePath.startsWith(codeCacheDir) - || filePath.startsWith(libDir) - || filePath.startsWith(nbFilesDir)) { + || filePath.startsWith(nbFilesDir) + || filePath.startsWith(deviceCacheDir) + || filePath.startsWith(deviceCodeCacheDir) + || filePath.startsWith(deviceNbFilesDir) + || filePath.startsWith(libDir)) { Log.w(TAG, "lib, cache, code_cache, and no_backup files are not backed up"); return; } @@ -504,11 +576,23 @@ public abstract class BackupAgent extends ContextWrapper { domain = FullBackup.SHAREDPREFS_TREE_TOKEN; rootpath = spDir; } else if (filePath.startsWith(filesDir)) { - domain = FullBackup.DATA_TREE_TOKEN; + domain = FullBackup.FILES_TREE_TOKEN; rootpath = filesDir; - } else if (filePath.startsWith(mainDir)) { + } else if (filePath.startsWith(rootDir)) { domain = FullBackup.ROOT_TREE_TOKEN; - rootpath = mainDir; + rootpath = rootDir; + } else if (filePath.startsWith(deviceDbDir)) { + domain = FullBackup.DEVICE_DATABASE_TREE_TOKEN; + rootpath = deviceDbDir; + } else if (filePath.startsWith(deviceSpDir)) { + domain = FullBackup.DEVICE_SHAREDPREFS_TREE_TOKEN; + rootpath = deviceSpDir; + } else if (filePath.startsWith(deviceFilesDir)) { + domain = FullBackup.DEVICE_FILES_TREE_TOKEN; + rootpath = deviceFilesDir; + } else if (filePath.startsWith(deviceRootDir)) { + domain = FullBackup.DEVICE_ROOT_TREE_TOKEN; + rootpath = deviceRootDir; } else if ((efDir != null) && filePath.startsWith(efDir)) { domain = FullBackup.MANAGED_EXTERNAL_TREE_TOKEN; rootpath = efDir; diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index 9ea2ba286754..cdc80e3ba3c1 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -55,13 +55,22 @@ public class FullBackup { public static final String APK_TREE_TOKEN = "a"; public static final String OBB_TREE_TOKEN = "obb"; + public static final String ROOT_TREE_TOKEN = "r"; - public static final String DATA_TREE_TOKEN = "f"; + public static final String FILES_TREE_TOKEN = "f"; public static final String NO_BACKUP_TREE_TOKEN = "nb"; public static final String DATABASE_TREE_TOKEN = "db"; public static final String SHAREDPREFS_TREE_TOKEN = "sp"; - public static final String MANAGED_EXTERNAL_TREE_TOKEN = "ef"; public static final String CACHE_TREE_TOKEN = "c"; + + public static final String DEVICE_ROOT_TREE_TOKEN = "d_r"; + public static final String DEVICE_FILES_TREE_TOKEN = "d_f"; + public static final String DEVICE_NO_BACKUP_TREE_TOKEN = "d_nb"; + public static final String DEVICE_DATABASE_TREE_TOKEN = "d_db"; + public static final String DEVICE_SHAREDPREFS_TREE_TOKEN = "d_sp"; + public static final String DEVICE_CACHE_TREE_TOKEN = "d_c"; + + public static final String MANAGED_EXTERNAL_TREE_TOKEN = "ef"; public static final String SHARED_STORAGE_TOKEN = "shared"; public static final String APPS_PREFIX = "apps/"; @@ -201,10 +210,18 @@ public class FullBackup { private final File DATABASE_DIR; private final File ROOT_DIR; private final File SHAREDPREF_DIR; - private final File EXTERNAL_DIR; private final File CACHE_DIR; private final File NOBACKUP_DIR; + private final File DEVICE_FILES_DIR; + private final File DEVICE_DATABASE_DIR; + private final File DEVICE_ROOT_DIR; + private final File DEVICE_SHAREDPREF_DIR; + private final File DEVICE_CACHE_DIR; + private final File DEVICE_NOBACKUP_DIR; + + private final File EXTERNAL_DIR; + final int mFullBackupContent; final PackageManager mPackageManager; final String mPackageName; @@ -214,7 +231,7 @@ public class FullBackup { */ String tokenToDirectoryPath(String domainToken) { try { - if (domainToken.equals(FullBackup.DATA_TREE_TOKEN)) { + if (domainToken.equals(FullBackup.FILES_TREE_TOKEN)) { return FILES_DIR.getCanonicalPath(); } else if (domainToken.equals(FullBackup.DATABASE_TREE_TOKEN)) { return DATABASE_DIR.getCanonicalPath(); @@ -224,14 +241,26 @@ public class FullBackup { return SHAREDPREF_DIR.getCanonicalPath(); } else if (domainToken.equals(FullBackup.CACHE_TREE_TOKEN)) { return CACHE_DIR.getCanonicalPath(); + } else if (domainToken.equals(FullBackup.NO_BACKUP_TREE_TOKEN)) { + return NOBACKUP_DIR.getCanonicalPath(); + } else if (domainToken.equals(FullBackup.DEVICE_FILES_TREE_TOKEN)) { + return DEVICE_FILES_DIR.getCanonicalPath(); + } else if (domainToken.equals(FullBackup.DEVICE_DATABASE_TREE_TOKEN)) { + return DEVICE_DATABASE_DIR.getCanonicalPath(); + } else if (domainToken.equals(FullBackup.DEVICE_ROOT_TREE_TOKEN)) { + return DEVICE_ROOT_DIR.getCanonicalPath(); + } else if (domainToken.equals(FullBackup.DEVICE_SHAREDPREFS_TREE_TOKEN)) { + return DEVICE_SHAREDPREF_DIR.getCanonicalPath(); + } else if (domainToken.equals(FullBackup.DEVICE_CACHE_TREE_TOKEN)) { + return DEVICE_CACHE_DIR.getCanonicalPath(); + } else if (domainToken.equals(FullBackup.DEVICE_NO_BACKUP_TREE_TOKEN)) { + return DEVICE_NOBACKUP_DIR.getCanonicalPath(); } else if (domainToken.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) { if (EXTERNAL_DIR != null) { return EXTERNAL_DIR.getCanonicalPath(); } else { return null; } - } else if (domainToken.equals(FullBackup.NO_BACKUP_TREE_TOKEN)) { - return NOBACKUP_DIR.getCanonicalPath(); } // Not a supported location Log.i(TAG, "Unrecognized domain " + domainToken); @@ -257,12 +286,25 @@ public class FullBackup { mFullBackupContent = context.getApplicationInfo().fullBackupContent; mPackageManager = context.getPackageManager(); mPackageName = context.getPackageName(); - FILES_DIR = context.getFilesDir(); - DATABASE_DIR = context.getDatabasePath("foo").getParentFile(); - ROOT_DIR = new File(context.getApplicationInfo().dataDir); - SHAREDPREF_DIR = context.getSharedPrefsFile("foo").getParentFile(); - CACHE_DIR = context.getCacheDir(); - NOBACKUP_DIR = context.getNoBackupFilesDir(); + + // System apps have control over where their default storage context + // is pointed, so we're always explicit when building paths. + final Context ceContext = context.createCredentialEncryptedStorageContext(); + FILES_DIR = ceContext.getFilesDir(); + DATABASE_DIR = ceContext.getDatabasePath("foo").getParentFile(); + ROOT_DIR = ceContext.getDataDir(); + SHAREDPREF_DIR = ceContext.getSharedPreferencesPath("foo").getParentFile(); + CACHE_DIR = ceContext.getCacheDir(); + NOBACKUP_DIR = ceContext.getNoBackupFilesDir(); + + final Context deContext = context.createDeviceEncryptedStorageContext(); + DEVICE_FILES_DIR = deContext.getFilesDir(); + DEVICE_DATABASE_DIR = deContext.getDatabasePath("foo").getParentFile(); + DEVICE_ROOT_DIR = deContext.getDataDir(); + DEVICE_SHAREDPREF_DIR = deContext.getSharedPreferencesPath("foo").getParentFile(); + DEVICE_CACHE_DIR = deContext.getCacheDir(); + DEVICE_NOBACKUP_DIR = deContext.getNoBackupFilesDir(); + if (android.os.Process.myUid() != Process.SYSTEM_UID) { EXTERNAL_DIR = context.getExternalFilesDir(null); } else { @@ -403,6 +445,13 @@ public class FullBackup { Log.v(TAG_XML_PARSER, "...automatically generated " + canonicalJournalPath + ". Ignore if nonexistent."); } + final String canonicalWalPath = + canonicalFile.getCanonicalPath() + "-wal"; + activeSet.add(canonicalWalPath); + if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) { + Log.v(TAG_XML_PARSER, "...automatically generated " + + canonicalWalPath + ". Ignore if nonexistent."); + } } // Special case for sharedpref files (not dirs) also add ".xml" suffix file. @@ -485,11 +534,19 @@ public class FullBackup { if ("root".equals(xmlDomain)) { return FullBackup.ROOT_TREE_TOKEN; } else if ("file".equals(xmlDomain)) { - return FullBackup.DATA_TREE_TOKEN; + return FullBackup.FILES_TREE_TOKEN; } else if ("database".equals(xmlDomain)) { return FullBackup.DATABASE_TREE_TOKEN; } else if ("sharedpref".equals(xmlDomain)) { return FullBackup.SHAREDPREFS_TREE_TOKEN; + } else if ("device_root".equals(xmlDomain)) { + return FullBackup.DEVICE_ROOT_TREE_TOKEN; + } else if ("device_file".equals(xmlDomain)) { + return FullBackup.DEVICE_FILES_TREE_TOKEN; + } else if ("device_database".equals(xmlDomain)) { + return FullBackup.DEVICE_DATABASE_TREE_TOKEN; + } else if ("device_sharedpref".equals(xmlDomain)) { + return FullBackup.DEVICE_SHAREDPREFS_TREE_TOKEN; } else if ("external".equals(xmlDomain)) { return FullBackup.MANAGED_EXTERNAL_TREE_TOKEN; } else { @@ -542,6 +599,14 @@ public class FullBackup { return ROOT_DIR; } else if ("sharedpref".equals(domain)) { return SHAREDPREF_DIR; + } else if ("device_file".equals(domain)) { + return DEVICE_FILES_DIR; + } else if ("device_database".equals(domain)) { + return DEVICE_DATABASE_DIR; + } else if ("device_root".equals(domain)) { + return DEVICE_ROOT_DIR; + } else if ("device_sharedpref".equals(domain)) { + return DEVICE_SHAREDPREF_DIR; } else if ("external".equals(domain)) { return EXTERNAL_DIR; } else { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index fff0c14884c0..0cdbef0b770b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -811,6 +811,25 @@ public abstract class Context { public abstract File getSharedPreferencesPath(String name); /** + * Returns the absolute path to the directory on the filesystem where all + * private files belonging to this app are stored. This is the top-level + * directory under which {@link #getFilesDir()}, {@link #getCacheDir()}, etc + * are contained. Apps should <em>not</em> create any files or directories + * as direct children of this directory, since it's a reserved namespace + * belonging to the platform. Instead, use {@link #getDir(String, int)} or + * other storage APIs. + * <p> + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + * <p> + * No additional permissions are required for the calling app to read or + * write files under the returned path. + * + * @see #getDir(String, int) + */ + public abstract File getDataDir(); + + /** * Returns the absolute path to the directory on the filesystem where files * created with {@link #openFileOutput} are stored. * <p> diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 61b87a957d2a..323c9bfe8099 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -212,6 +212,11 @@ public class ContextWrapper extends Context { } @Override + public File getDataDir() { + return mBase.getDataDir(); + } + + @Override public File getFilesDir() { return mBase.getFilesDir(); } diff --git a/core/tests/coretests/src/android/app/backup/FullBackupTest.java b/core/tests/coretests/src/android/app/backup/FullBackupTest.java index c3afbf672ae2..3869cd29f69f 100644 --- a/core/tests/coretests/src/android/app/backup/FullBackupTest.java +++ b/core/tests/coretests/src/android/app/backup/FullBackupTest.java @@ -66,7 +66,7 @@ public class FullBackupTest extends AndroidTestCase { assertEquals("Excluding files when there was no <exclude/> tag.", 0, excludesSet.size()); assertEquals("Unexpected number of <include/>s", 1, includeMap.size()); - Set<String> fileDomainIncludes = includeMap.get(FullBackup.DATA_TREE_TOKEN); + Set<String> fileDomainIncludes = includeMap.get(FullBackup.FILES_TREE_TOKEN); assertEquals("Didn't find expected file domain include.", 1, fileDomainIncludes.size()); assertEquals("Invalid path parsed for <include/>", new File(mContext.getFilesDir(), "onlyInclude.txt").getCanonicalPath(), @@ -99,7 +99,7 @@ public class FullBackupTest extends AndroidTestCase { FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); - Set<String> fileDomainIncludes = includeMap.get(FullBackup.DATA_TREE_TOKEN); + Set<String> fileDomainIncludes = includeMap.get(FullBackup.FILES_TREE_TOKEN); assertEquals("Didn't find expected file domain include.", 1, fileDomainIncludes.size()); assertEquals("Invalid path parsed for <include/>", new File(mContext.getFilesDir(), "include.txt").getCanonicalPath(), @@ -128,15 +128,16 @@ public class FullBackupTest extends AndroidTestCase { FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext); bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap); - Set<String> fileDomainIncludes = includeMap.get(FullBackup.DATA_TREE_TOKEN); + Set<String> fileDomainIncludes = includeMap.get(FullBackup.FILES_TREE_TOKEN); assertEquals("Didn't find expected file domain include.", 1, fileDomainIncludes.size()); assertEquals("Invalid path parsed for <include/>", new File(mContext.getFilesDir(), "include1.txt").getCanonicalPath(), fileDomainIncludes.iterator().next()); Set<String> databaseDomainIncludes = includeMap.get(FullBackup.DATABASE_TREE_TOKEN); + // Three expected here because of "-journal" and "-wal" files assertEquals("Didn't find expected database domain include.", - 2, databaseDomainIncludes.size()); // two expected here because of "-journal" file + 3, databaseDomainIncludes.size()); assertTrue("Invalid path parsed for <include/>", databaseDomainIncludes.contains( new File(mContext.getDatabasePath("foo").getParentFile(), "include2.txt") @@ -147,6 +148,12 @@ public class FullBackupTest extends AndroidTestCase { mContext.getDatabasePath("foo").getParentFile(), "include2.txt-journal") .getCanonicalPath())); + assertTrue("Invalid path parsed for <include/>", + databaseDomainIncludes.contains( + new File( + mContext.getDatabasePath("foo").getParentFile(), + "include2.txt-wal") + .getCanonicalPath())); List<String> sharedPrefDomainIncludes = new ArrayList<String>( includeMap.get(FullBackup.SHAREDPREFS_TREE_TOKEN)); @@ -168,7 +175,7 @@ public class FullBackupTest extends AndroidTestCase { sharedPrefDomainIncludes.get(2)); - assertEquals("Unexpected number of <exclude/>s", 6, excludesSet.size()); + assertEquals("Unexpected number of <exclude/>s", 7, excludesSet.size()); // Sets are annoying to iterate over b/c order isn't enforced - convert to an array and // sort lexicographically. List<String> arrayedSet = new ArrayList<String>(excludesSet); @@ -183,20 +190,24 @@ public class FullBackupTest extends AndroidTestCase { .getCanonicalPath(), arrayedSet.get(1)); assertEquals("Invalid path parsed for <exclude/>", - new File(mContext.getFilesDir(), "exclude1.txt").getCanonicalPath(), + new File(mContext.getDatabasePath("foo").getParentFile(), "exclude2.txt-wal") + .getCanonicalPath(), arrayedSet.get(2)); assertEquals("Invalid path parsed for <exclude/>", + new File(mContext.getFilesDir(), "exclude1.txt").getCanonicalPath(), + arrayedSet.get(3)); + assertEquals("Invalid path parsed for <exclude/>", new File(mContext.getSharedPrefsFile("foo").getParentFile(), "exclude3") .getCanonicalPath(), - arrayedSet.get(3)); + arrayedSet.get(4)); assertEquals("Invalid path parsed for <exclude/>", new File(mContext.getSharedPrefsFile("foo").getParentFile(), "exclude3.xml") .getCanonicalPath(), - arrayedSet.get(4)); + arrayedSet.get(5)); assertEquals("Invalid path parsed for <exclude/>", new File(mContext.getSharedPrefsFile("foo").getParentFile(), "exclude4.xml") .getCanonicalPath(), - arrayedSet.get(5)); + arrayedSet.get(6)); } public void testParseBackupSchemeFromXml_invalidXmlFails() throws Exception { @@ -247,7 +258,7 @@ public class FullBackupTest extends AndroidTestCase { assertEquals("Didn't throw away invalid \"..\" path.", 0, includeMap.size()); - Set<String> fileDomainIncludes = includeMap.get(FullBackup.DATA_TREE_TOKEN); + Set<String> fileDomainIncludes = includeMap.get(FullBackup.FILES_TREE_TOKEN); assertNull("Didn't throw away invalid \"..\" path.", fileDomainIncludes); } public void testDoubleDotInPath_isIgnored() throws Exception { @@ -261,7 +272,7 @@ public class FullBackupTest extends AndroidTestCase { assertEquals("Didn't throw away invalid \"..\" path.", 0, includeMap.size()); - Set<String> fileDomainIncludes = includeMap.get(FullBackup.DATA_TREE_TOKEN); + Set<String> fileDomainIncludes = includeMap.get(FullBackup.FILES_TREE_TOKEN); assertNull("Didn't throw away invalid \"..\" path.", fileDomainIncludes); } diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index eaee1d3e0602..a4fc2ecdc520 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -191,7 +191,8 @@ public class BackupManagerService { // 1 : initial release // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection // 3 : introduced "_meta" metadata file; no other format change per se - static final int BACKUP_FILE_VERSION = 3; + // 4 : added support for new device-encrypted storage locations + static final int BACKUP_FILE_VERSION = 4; static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n"; static final int BACKUP_PW_FILE_VERSION = 2; static final String BACKUP_METADATA_FILENAME = "_meta"; @@ -347,13 +348,13 @@ public class BackupManagerService { } @Override - public void onBootPhase(int phase) { - if (phase == PHASE_SYSTEM_SERVICES_READY) { - sInstance.initialize(UserHandle.USER_SYSTEM); - } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) { + public void onUnlockUser(int userId) { + if (userId == UserHandle.USER_SYSTEM) { + sInstance.initialize(userId); + ContentResolver r = sInstance.mContext.getContentResolver(); - boolean areEnabled = Settings.Secure.getInt(r, - Settings.Secure.BACKUP_ENABLED, 0) != 0; + boolean areEnabled = Settings.Secure.getIntForUser(r, + Settings.Secure.BACKUP_ENABLED, 0, userId) != 0; try { sInstance.setBackupEnabled(areEnabled); } catch (RemoteException e) { diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java index bec1e4f76b9b..84cffe17d1d2 100644 --- a/test-runner/src/android/test/mock/MockContext.java +++ b/test-runner/src/android/test/mock/MockContext.java @@ -191,6 +191,11 @@ public class MockContext extends Context { } @Override + public File getDataDir() { + throw new UnsupportedOperationException(); + } + + @Override public File getFilesDir() { throw new UnsupportedOperationException(); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index 4b89217a581e..17ab2ff5cf5e 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -1382,6 +1382,12 @@ public final class BridgeContext extends Context { } @Override + public File getDataDir() { + // pass + return null; + } + + @Override public File getFilesDir() { // pass return null; |