Improve performance of storage measurement.
When calculating directory sizes of data living on emulated external storage, translate the path to use the internal backing data, which avoids going through the emulation layer. It carefully retreats to the original path when it runs into trouble. Testing with a hierarchy of 10 directories deep and 2 directories and 10 files wide at each level, this change improves performance from 5900ms before to 250ms after; over 20 times faster (!). Bug: 8172425 Change-Id: Ia7365416f091e102bf7345a49f7d7209a22580a9
This commit is contained in:
@ -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 <em>only</em> suitable
|
||||
* for read-only access.
|
||||
* <p>
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@
|
||||
<uses-permission android:name="android.permission.ASEC_DESTROY"/>
|
||||
<uses-permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<!-- Used to improve MeasureUtils performance on emulated storage -->
|
||||
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
|
||||
<uses-permission android:name="android.permission.ACCESS_ALL_EXTERNAL_STORAGE" />
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user