diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 1bada67c4aad4..d9846ecb2b214 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -25,6 +25,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.io.File; +import java.io.IOException; /** * Provides access to environment variables. @@ -36,12 +37,16 @@ public class Environment { private static final String ENV_EMULATED_STORAGE_SOURCE = "EMULATED_STORAGE_SOURCE"; private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET"; private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE"; + private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT"; /** {@hide} */ public static String DIRECTORY_ANDROID = "Android"; - private static final File ROOT_DIRECTORY - = getDirectory("ANDROID_ROOT", "/system"); + private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system"); + private static final File DIR_MEDIA_STORAGE = getDirectory(ENV_MEDIA_STORAGE, "/data/media"); + + private static final String CANONCIAL_EMULATED_STORAGE_TARGET = getCanonicalPathOrNull( + ENV_EMULATED_STORAGE_TARGET); private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled"; @@ -178,7 +183,7 @@ public class Environment { * Gets the Android root directory. */ public static File getRootDirectory() { - return ROOT_DIRECTORY; + return DIR_ANDROID_ROOT; } /** @@ -632,6 +637,19 @@ public class Environment { return path == null ? new File(defaultPath) : new File(path); } + private static String getCanonicalPathOrNull(String variableName) { + String path = System.getenv(variableName); + if (path == null) { + return null; + } + try { + return new File(path).getCanonicalPath(); + } catch (IOException e) { + Log.w(TAG, "Unable to resolve canonical path for " + path); + return null; + } + } + private static void throwIfSystem() { if (Process.myUid() == Process.SYSTEM_UID) { Log.wtf(TAG, "Static storage paths aren't available from AID_SYSTEM", new Throwable()); @@ -649,4 +667,40 @@ public class Environment { } return cur; } + + /** + * If the given path exists on emulated external storage, return the + * translated backing path hosted on internal storage. This bypasses any + * emulation later, improving performance. This is only suitable + * for read-only access. + *

+ * Returns original path if given path doesn't meet these criteria. Callers + * must hold {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} + * permission. + * + * @hide + */ + public static File maybeTranslateEmulatedPathToInternal(File path) { + // Fast return if not emulated, or missing variables + if (!Environment.isExternalStorageEmulated() + || CANONCIAL_EMULATED_STORAGE_TARGET == null) { + return path; + } + + try { + final String rawPath = path.getCanonicalPath(); + if (rawPath.startsWith(CANONCIAL_EMULATED_STORAGE_TARGET)) { + final File internalPath = new File(DIR_MEDIA_STORAGE, + rawPath.substring(CANONCIAL_EMULATED_STORAGE_TARGET.length())); + if (internalPath.exists()) { + return internalPath; + } + } + } catch (IOException e) { + Log.w(TAG, "Failed to resolve canonical path for " + path); + } + + // Unable to translate to internal path; use original + return path; + } } diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml index 3dcd232915a45..57c87e4167a8e 100644 --- a/packages/DefaultContainerService/AndroidManifest.xml +++ b/packages/DefaultContainerService/AndroidManifest.xml @@ -7,6 +7,8 @@ + + diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 731a09cfdcb74..de77cac00a112 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -16,16 +16,12 @@ package com.android.defcontainer; -import com.android.internal.app.IMediaContainerService; -import com.android.internal.content.NativeLibraryHelper; -import com.android.internal.content.PackageHelper; - import android.app.IntentService; import android.content.Intent; -import android.content.pm.MacAuthenticatedInputStream; import android.content.pm.ContainerEncryptionParams; import android.content.pm.IPackageManager; import android.content.pm.LimitedLengthInputStream; +import android.content.pm.MacAuthenticatedInputStream; import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; @@ -43,10 +39,16 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StatFs; +import android.os.SystemClock; import android.provider.Settings; import android.util.DisplayMetrics; +import android.util.Log; import android.util.Slog; +import com.android.internal.app.IMediaContainerService; +import com.android.internal.content.NativeLibraryHelper; +import com.android.internal.content.PackageHelper; + import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; @@ -228,9 +230,10 @@ public class DefaultContainerService extends IntentService { public long calculateDirectorySize(String path) throws RemoteException { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - final File directory = new File(path); - if (directory.exists() && directory.isDirectory()) { - return MeasurementUtils.measureDirectory(path); + final File dir = Environment.maybeTranslateEmulatedPathToInternal(new File(path)); + if (dir.exists() && dir.isDirectory()) { + final String targetPath = dir.getAbsolutePath(); + return MeasurementUtils.measureDirectory(targetPath); } else { return 0L; }