OSDN Git Service

Make BackupManager encryption aware.
authorJeff Sharkey <jsharkey@android.com>
Wed, 17 Feb 2016 22:29:38 +0000 (15:29 -0700)
committerJeff Sharkey <jsharkey@android.com>
Thu, 18 Feb 2016 17:54:11 +0000 (10:54 -0700)
Backup requires both CE and DE storage to be available, so delay
spinning up the backup system until the user is unlocked, since
that's when CE storage becomes available.  Note that devices without
FBE immediately transition USER_SYSTEM into the unlocked state,
since their CE is always available.

Offer to backup and restore files under both CE and DE.  Since DE
is effectively the same as CE, most logic is simply duplicated for
now, but it could be simplified in the future.  Since system apps
can force their default storage location to DE, we always build
explicit CE and DE paths.

Add getDataDir() to give clean access to the top-level private data
directory, but disclaim that apps shouldn't create files there.

Bug: 26279618
Change-Id: Ic34a4b330223725db93b1d0f5c9dffc88002c61f

12 files changed:
api/current.txt
api/system-current.txt
api/test-current.txt
core/java/android/app/ContextImpl.java
core/java/android/app/backup/BackupAgent.java
core/java/android/app/backup/FullBackup.java
core/java/android/content/Context.java
core/java/android/content/ContextWrapper.java
core/tests/coretests/src/android/app/backup/FullBackupTest.java
services/backup/java/com/android/server/backup/BackupManagerService.java
test-runner/src/android/test/mock/MockContext.java
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java

index 6110d1c..1ebec4e 100644 (file)
@@ -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();
index c01f1c3..55df364 100644 (file)
@@ -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();
index 96d29d1..8fcb9bd 100644 (file)
@@ -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();
index 5e8d190..8884949 100644 (file)
@@ -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,
index 63f1425..aeb3156 100644 (file)
@@ -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;
index 9ea2ba2..cdc80e3 100644 (file)
@@ -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 {
index fff0c14..0cdbef0 100644 (file)
@@ -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>
index 61b87a9..323c9bf 100644 (file)
@@ -212,6 +212,11 @@ public class ContextWrapper extends Context {
     }
 
     @Override
+    public File getDataDir() {
+        return mBase.getDataDir();
+    }
+
+    @Override
     public File getFilesDir() {
         return mBase.getFilesDir();
     }
index c3afbf6..3869cd2 100644 (file)
@@ -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);
     }
 
index eaee1d3..a4fc2ec 100644 (file)
@@ -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) {
index bec1e4f..84cffe1 100644 (file)
@@ -191,6 +191,11 @@ public class MockContext extends Context {
     }
 
     @Override
+    public File getDataDir() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public File getFilesDir() {
         throw new UnsupportedOperationException();
     }
index 4b89217..17ab2ff 100644 (file)
@@ -1382,6 +1382,12 @@ public final class BridgeContext extends Context {
     }
 
     @Override
+    public File getDataDir() {
+        // pass
+        return null;
+    }
+
+    @Override
     public File getFilesDir() {
         // pass
         return null;