diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 9ad33a5a4105a..b678df748748d 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -227,6 +227,7 @@ public abstract class BackupAgent extends ContextWrapper { String libDir = (appInfo.nativeLibraryDir != null) ? new File(appInfo.nativeLibraryDir).getCanonicalPath() : null; + String externalFilesDir = getExternalFilesDir(null).getCanonicalPath(); // Filters, the scan queue, and the set of resulting entities HashSet filterSet = new HashSet(); @@ -254,6 +255,12 @@ public abstract class BackupAgent extends ContextWrapper { filterSet.add(databaseDir); filterSet.remove(sharedPrefsDir); fullBackupFileTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, sharedPrefsDir, filterSet, data); + + // 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 + // IOExceptions and similar, and treats them as non-fatal, so we rely on that here. + fullBackupFileTree(packageName, FullBackup.MANAGED_EXTERNAL_TREE_TOKEN, externalFilesDir, null, data); } /** @@ -274,6 +281,7 @@ public abstract class BackupAgent extends ContextWrapper { String spDir; String cacheDir; String libDir; + String efDir; String filePath; ApplicationInfo appInfo = getApplicationInfo(); @@ -287,6 +295,7 @@ public abstract class BackupAgent extends ContextWrapper { libDir = (appInfo.nativeLibraryDir == null) ? null : new File(appInfo.nativeLibraryDir).getCanonicalPath(); + efDir = getExternalFilesDir(null).getCanonicalPath(); // Now figure out which well-defined tree the file is placed in, working from // most to least specific. We also specifically exclude the lib and cache dirs. @@ -315,6 +324,9 @@ public abstract class BackupAgent extends ContextWrapper { } else if (filePath.startsWith(mainDir)) { domain = FullBackup.ROOT_TREE_TOKEN; rootpath = mainDir; + } else if (filePath.startsWith(efDir)) { + domain = FullBackup.MANAGED_EXTERNAL_TREE_TOKEN; + rootpath = efDir; } else { Log.w(TAG, "File " + filePath + " is in an unsupported location; skipping"); return; @@ -438,6 +450,8 @@ public abstract class BackupAgent extends ContextWrapper { basePath = getSharedPrefsFile("foo").getParentFile().getCanonicalPath(); } else if (domain.equals(FullBackup.CACHE_TREE_TOKEN)) { basePath = getCacheDir().getCanonicalPath(); + } else if (domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) { + basePath = getExternalFilesDir(null).getCanonicalPath(); } else { // Not a supported location Log.i(TAG, "Data restored from non-app domain " + domain + ", ignoring"); diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java index f859599e18ce2..2fe08f34b49e0 100644 --- a/core/java/android/app/backup/FullBackup.java +++ b/core/java/android/app/backup/FullBackup.java @@ -46,6 +46,7 @@ public class FullBackup { public static final String DATA_TREE_TOKEN = "f"; 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 SHARED_STORAGE_TOKEN = "shared"; diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java index a6415b2fb8b4b..89f84fcf953e9 100644 --- a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java +++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java @@ -4,6 +4,7 @@ import android.app.backup.FullBackupAgent; import android.app.backup.FullBackup; import android.app.backup.FullBackupDataOutput; import android.content.Context; +import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; @@ -11,6 +12,7 @@ import android.util.Slog; import java.io.File; import java.io.IOException; +import java.util.HashSet; public class SharedStorageAgent extends FullBackupAgent { static final String TAG = "SharedStorageAgent"; @@ -38,13 +40,20 @@ public class SharedStorageAgent extends FullBackupAgent { // "primary" shared storage volume is first in the list. if (mVolumes != null) { if (DEBUG) Slog.i(TAG, "Backing up " + mVolumes.length + " shared volumes"); + // Ignore all apps' getExternalFilesDir() content; it is backed up as part of + // each app-specific payload. + HashSet externalFilesDirFilter = new HashSet(); + final File externalAndroidRoot = new File(Environment.getExternalStorageDirectory(), + Environment.DIRECTORY_ANDROID); + externalFilesDirFilter.add(externalAndroidRoot.getCanonicalPath()); + for (int i = 0; i < mVolumes.length; i++) { StorageVolume v = mVolumes[i]; // Express the contents of volume N this way in the tar stream: // shared/N/path/to/file // The restore will then extract to the given volume String domain = FullBackup.SHARED_PREFIX + i; - fullBackupFileTree(null, domain, v.getPath(), null, output); + fullBackupFileTree(null, domain, v.getPath(), externalFilesDirFilter, output); } } }