diff options
| -rw-r--r-- | api/system-current.txt | 6 | ||||
| -rw-r--r-- | core/java/android/content/pm/dex/ArtManager.java | 109 | ||||
| -rw-r--r-- | core/java/android/content/pm/dex/IArtManager.aidl | 38 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/Installer.java | 6 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/dex/ArtManagerService.java | 101 |
5 files changed, 175 insertions, 85 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index b6d8142b2ebd..d2c6bd064ac9 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -953,8 +953,10 @@ package android.content.pm { package android.content.pm.dex { public class ArtManager { - method public boolean isRuntimeProfilingEnabled(); - method public void snapshotRuntimeProfile(java.lang.String, java.lang.String, android.content.pm.dex.ArtManager.SnapshotRuntimeProfileCallback, android.os.Handler); + method public boolean isRuntimeProfilingEnabled(int); + method public void snapshotRuntimeProfile(int, java.lang.String, java.lang.String, java.util.concurrent.Executor, android.content.pm.dex.ArtManager.SnapshotRuntimeProfileCallback); + field public static final int PROFILE_APPS = 0; // 0x0 + field public static final int PROFILE_BOOT_IMAGE = 1; // 0x1 field public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1; // 0x1 field public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2; // 0x2 field public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0; // 0x0 diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java index f979576e5315..075306366264 100644 --- a/core/java/android/content/pm/dex/ArtManager.java +++ b/core/java/android/content/pm/dex/ArtManager.java @@ -16,18 +16,20 @@ package android.content.pm.dex; +import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.os.Environment; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Slog; import java.io.File; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; /** * Class for retrieving various kinds of information related to the runtime artifacts of @@ -46,6 +48,20 @@ public class ArtManager { /** The snapshot failed because of an internal error (e.g. error during opening profiles). */ public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2; + /** Constant used for applications profiles. */ + public static final int PROFILE_APPS = 0; + /** Constant used for the boot image profile. */ + public static final int PROFILE_BOOT_IMAGE = 1; + + /** @hide */ + @IntDef(flag = true, prefix = { "PROFILE_" }, value = { + PROFILE_APPS, + PROFILE_BOOT_IMAGE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ProfileType {} + + private IArtManager mArtManager; /** @@ -56,41 +72,59 @@ public class ArtManager { } /** - * Snapshots the runtime profile for an apk belonging to the package {@code packageName}. - * The apk is identified by {@code codePath}. The calling process must have - * {@code android.permission.READ_RUNTIME_PROFILE} permission. + * Snapshots a runtime profile according to the {@code profileType} parameter. + * + * If {@code profileType} is {@link ArtManager#PROFILE_APPS} the method will snapshot + * the profile for for an apk belonging to the package {@code packageName}. + * The apk is identified by {@code codePath}. + * + * If {@code profileType} is {@code ArtManager.PROFILE_BOOT_IMAGE} the method will snapshot + * the profile for the boot image. In this case {@code codePath can be null}. The parameters + * {@code packageName} and {@code codePath} are ignored. + *u + * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission. * - * The result will be posted on {@code handler} using the given {@code callback}. - * The profile being available as a read-only {@link android.os.ParcelFileDescriptor}. + * The result will be posted on the {@code executor} using the given {@code callback}. + * The profile will be available as a read-only {@link android.os.ParcelFileDescriptor}. * - * @param packageName the target package name - * @param codePath the code path for which the profile should be retrieved + * This method will throw {@link IllegalStateException} if + * {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given + * {@code profileType}. + * + * @param profileType the type of profile that should be snapshot (boot image or app) + * @param packageName the target package name or null if the target is the boot image + * @param codePath the code path for which the profile should be retrieved or null if + * the target is the boot image * @param callback the callback which should be used for the result - * @param handler the handler which should be used to post the result + * @param executor the executor which should be used to post the result */ @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES) - public void snapshotRuntimeProfile(@NonNull String packageName, @NonNull String codePath, - @NonNull SnapshotRuntimeProfileCallback callback, @NonNull Handler handler) { + public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName, + @Nullable String codePath, @NonNull Executor executor, + @NonNull SnapshotRuntimeProfileCallback callback) { Slog.d(TAG, "Requesting profile snapshot for " + packageName + ":" + codePath); SnapshotRuntimeProfileCallbackDelegate delegate = - new SnapshotRuntimeProfileCallbackDelegate(callback, handler.getLooper()); + new SnapshotRuntimeProfileCallbackDelegate(callback, executor); try { - mArtManager.snapshotRuntimeProfile(packageName, codePath, delegate); + mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } } /** - * Returns true if runtime profiles are enabled, false otherwise. + * Returns true if runtime profiles are enabled for the given type, false otherwise. * * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission. + * + * @param profileType can be either {@link ArtManager#PROFILE_APPS} + * or {@link ArtManager#PROFILE_BOOT_IMAGE} */ @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES) - public boolean isRuntimeProfilingEnabled() { + public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) { try { - return mArtManager.isRuntimeProfilingEnabled(); + return mArtManager.isRuntimeProfilingEnabled(profileType); } catch (RemoteException e) { e.rethrowAsRuntimeException(); } @@ -119,41 +153,24 @@ public class ArtManager { } private static class SnapshotRuntimeProfileCallbackDelegate - extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub - implements Handler.Callback { - private static final int MSG_SNAPSHOT_OK = 1; - private static final int MSG_ERROR = 2; + extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub { private final ArtManager.SnapshotRuntimeProfileCallback mCallback; - private final Handler mHandler; + private final Executor mExecutor; private SnapshotRuntimeProfileCallbackDelegate( - ArtManager.SnapshotRuntimeProfileCallback callback, Looper looper) { + ArtManager.SnapshotRuntimeProfileCallback callback, Executor executor) { mCallback = callback; - mHandler = new Handler(looper, this); + mExecutor = executor; } @Override - public void onSuccess(ParcelFileDescriptor profileReadFd) { - mHandler.obtainMessage(MSG_SNAPSHOT_OK, profileReadFd).sendToTarget(); + public void onSuccess(final ParcelFileDescriptor profileReadFd) { + mExecutor.execute(() -> mCallback.onSuccess(profileReadFd)); } @Override public void onError(int errCode) { - mHandler.obtainMessage(MSG_ERROR, errCode, 0).sendToTarget(); - } - - @Override - public boolean handleMessage(Message msg) { - switch (msg.what) { - case MSG_SNAPSHOT_OK: - mCallback.onSuccess((ParcelFileDescriptor) msg.obj); - break; - case MSG_ERROR: - mCallback.onError(msg.arg1); - break; - default: return false; - } - return true; + mExecutor.execute(() -> mCallback.onError(errCode)); } } @@ -178,17 +195,15 @@ public class ArtManager { } /** - * Return the snapshot profile file for the given package and split. + * Return the snapshot profile file for the given package and profile name. * * KEEP in sync with installd dexopt.cpp. * TODO(calin): inject the snapshot profile name from PM to avoid the dependency. * * @hide */ - public static File getProfileSnapshotFile(String packageName, String splitName) { + public static File getProfileSnapshotFileForName(String packageName, String profileName) { File profileDir = Environment.getDataRefProfilesDePackageDirectory(packageName); - String snapshotFile = getProfileName(splitName) + ".snapshot"; - return new File(profileDir, snapshotFile); - + return new File(profileDir, profileName + ".snapshot"); } } diff --git a/core/java/android/content/pm/dex/IArtManager.aidl b/core/java/android/content/pm/dex/IArtManager.aidl index 8cbb452344b2..6abfdbab6e26 100644 --- a/core/java/android/content/pm/dex/IArtManager.aidl +++ b/core/java/android/content/pm/dex/IArtManager.aidl @@ -25,20 +25,34 @@ import android.content.pm.dex.ISnapshotRuntimeProfileCallback; */ interface IArtManager { /** - * Snapshots the runtime profile for an apk belonging to the package {@param packageName}. - * The apk is identified by {@param codePath}. The calling process must have - * {@code android.permission.READ_RUNTIME_PROFILE} permission. + * Snapshots a runtime profile according to the {@code profileType} parameter. * - * The result will be posted on {@param callback} with the profile being available as a - * read-only {@link android.os.ParcelFileDescriptor}. - */ - oneway void snapshotRuntimeProfile(in String packageName, - in String codePath, in ISnapshotRuntimeProfileCallback callback); - - /** - * Returns true if runtime profiles are enabled, false otherwise. + * If {@code profileType} is {@link ArtManager#PROFILE_APPS} the method will snapshot + * the profile for for an apk belonging to the package {@code packageName}. + * The apk is identified by {@code codePath}. + * + * If {@code profileType} is {@code ArtManager.PROFILE_BOOT_IMAGE} the method will snapshot + * the profile for the boot image. In this case {@code codePath can be null}. The parameters + * {@code packageName} and {@code codePath} are ignored. * * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission. + * + * The result will be posted on the {@code executor} using the given {@code callback}. + * The profile will be available as a read-only {@link android.os.ParcelFileDescriptor}. + * + * This method will throw {@link IllegalStateException} if + * {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given + * {@code profileType}. */ - boolean isRuntimeProfilingEnabled(); + oneway void snapshotRuntimeProfile(int profileType, in String packageName, + in String codePath, in ISnapshotRuntimeProfileCallback callback); + + /** + * Returns true if runtime profiles are enabled for the given type, false otherwise. + * The type can be can be either {@code ArtManager.PROFILE_APPS} + * or {@code ArtManager.PROFILE_BOOT_IMAGE}. + * + * @param profileType + */ + boolean isRuntimeProfilingEnabled(int profileType); } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 7b02ace2c05f..1c1357bb23fa 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -502,11 +502,11 @@ public class Installer extends SystemService { } } - public boolean createProfileSnapshot(int appId, String packageName, String profileName) - throws InstallerException { + public boolean createProfileSnapshot(int appId, String packageName, String profileName, + String classpath) throws InstallerException { if (!checkBeforeRemote()) return false; try { - return mInstalld.createProfileSnapshot(appId, packageName, profileName); + return mInstalld.createProfileSnapshot(appId, packageName, profileName, classpath); } catch (Exception e) { throw InstallerException.from(e); } diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index f15dc2d5f8d3..e29027288e9b 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -23,8 +23,10 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.dex.ArtManager; +import android.content.pm.dex.ArtManager.ProfileType; import android.content.pm.dex.DexMetadataHelper; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.ParcelFileDescriptor; import android.os.RemoteException; @@ -32,6 +34,7 @@ import android.content.pm.IPackageManager; import android.content.pm.dex.ISnapshotRuntimeProfileCallback; import android.os.SystemProperties; import android.os.UserHandle; +import android.system.Os; import android.util.ArrayMap; import android.util.Slog; @@ -43,6 +46,9 @@ import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; import java.io.File; import java.io.FileNotFoundException; +import libcore.io.IoUtils; +import libcore.util.NonNull; +import libcore.util.Nullable; /** * A system service that provides access to runtime and compiler artifacts. @@ -62,6 +68,12 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { private static boolean DEBUG = false; private static boolean DEBUG_IGNORE_PERMISSIONS = false; + // Package name used to create the profile directory layout when + // taking a snapshot of the boot image profile. + private static final String BOOT_IMAGE_ANDROID_PACKAGE = "android"; + // Profile name used for the boot image profile. + private static final String BOOT_IMAGE_PROFILE_NAME = "android.prof"; + private final IPackageManager mPackageManager; private final Object mInstallLock; @GuardedBy("mInstallLock") @@ -77,20 +89,36 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { } @Override - public void snapshotRuntimeProfile(String packageName, String codePath, - ISnapshotRuntimeProfileCallback callback) { + public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName, + @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback) { // Sanity checks on the arguments. - Preconditions.checkStringNotEmpty(packageName); - Preconditions.checkStringNotEmpty(codePath); Preconditions.checkNotNull(callback); - // Verify that the caller has the right permissions. - checkReadRuntimeProfilePermission(); + boolean bootImageProfile = profileType == ArtManager.PROFILE_BOOT_IMAGE; + if (!bootImageProfile) { + Preconditions.checkStringNotEmpty(codePath); + Preconditions.checkStringNotEmpty(packageName); + } + + // Verify that the caller has the right permissions and that the runtime profiling is + // enabled. The call to isRuntimePermissions will checkReadRuntimeProfilePermission. + if (!isRuntimeProfilingEnabled(profileType)) { + throw new IllegalStateException("Runtime profiling is not enabled for " + profileType); + } if (DEBUG) { Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath); } + if (bootImageProfile) { + snapshotBootImageProfile(callback); + } else { + snapshotAppProfile(packageName, codePath, callback); + } + } + + private void snapshotAppProfile(String packageName, String codePath, + ISnapshotRuntimeProfileCallback callback) { PackageInfo info = null; try { // Note that we use the default user 0 to retrieve the package info. @@ -127,18 +155,25 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { } // All good, create the profile snapshot. - createProfileSnapshot(packageName, splitName, callback, info); + int appId = UserHandle.getAppId(info.applicationInfo.uid); + if (appId < 0) { + postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); + Slog.wtf(TAG, "AppId is -1 for package: " + packageName); + return; + } + + createProfileSnapshot(packageName, ArtManager.getProfileName(splitName), codePath, + appId, callback); // Destroy the snapshot, we no longer need it. - destroyProfileSnapshot(packageName, splitName); + destroyProfileSnapshot(packageName, ArtManager.getProfileName(splitName)); } - private void createProfileSnapshot(String packageName, String splitName, - ISnapshotRuntimeProfileCallback callback, PackageInfo info) { + private void createProfileSnapshot(String packageName, String profileName, String classpath, + int appId, ISnapshotRuntimeProfileCallback callback) { // Ask the installer to snapshot the profile. synchronized (mInstallLock) { try { - if (!mInstaller.createProfileSnapshot(UserHandle.getAppId(info.applicationInfo.uid), - packageName, ArtManager.getProfileName(splitName))) { + if (!mInstaller.createProfileSnapshot(appId, packageName, profileName, classpath)) { postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); return; } @@ -149,8 +184,9 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { } // Open the snapshot and invoke the callback. - File snapshotProfile = ArtManager.getProfileSnapshotFile(packageName, splitName); - ParcelFileDescriptor fd; + File snapshotProfile = ArtManager.getProfileSnapshotFileForName(packageName, profileName); + + ParcelFileDescriptor fd = null; try { fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY); postSuccess(packageName, fd, callback); @@ -158,31 +194,54 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":" + snapshotProfile, e); postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); + } finally { + IoUtils.closeQuietly(fd); } } - private void destroyProfileSnapshot(String packageName, String splitName) { + private void destroyProfileSnapshot(String packageName, String profileName) { if (DEBUG) { - Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + splitName); + Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + profileName); } synchronized (mInstallLock) { try { - mInstaller.destroyProfileSnapshot(packageName, - ArtManager.getProfileName(splitName)); + mInstaller.destroyProfileSnapshot(packageName, profileName); } catch (InstallerException e) { Slog.e(TAG, "Failed to destroy profile snapshot for " + - packageName + ":" + splitName, e); + packageName + ":" + profileName, e); } } } @Override - public boolean isRuntimeProfilingEnabled() { + public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) { // Verify that the caller has the right permissions. checkReadRuntimeProfilePermission(); - return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false); + switch (profileType) { + case ArtManager.PROFILE_APPS : + return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false); + case ArtManager.PROFILE_BOOT_IMAGE: + return (Build.IS_USERDEBUG || Build.IS_ENG) && + SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false) && + SystemProperties.getBoolean("dalvik.vm.profilebootimage", false); + default: + throw new IllegalArgumentException("Invalid profile type:" + profileType); + } + } + + private void snapshotBootImageProfile(ISnapshotRuntimeProfileCallback callback) { + // Combine the profiles for boot classpath and system server classpath. + // This avoids having yet another type of profiles and simplifies the processing. + String classpath = String.join(":", Os.getenv("BOOTCLASSPATH"), + Os.getenv("SYSTEMSERVERCLASSPATH")); + + // Create the snapshot. + createProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME, classpath, + /*appId*/ -1, callback); + // Destroy the snapshot, we no longer need it. + destroyProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME); } /** |