Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e4ad74e8 authored by Calin Juravle's avatar Calin Juravle Committed by Andreas Gampe
Browse files

Extend the ArtManager profiling API to cover boot image profiling

The boot image profile is available only on userdebug and eng builds and
combines the boot classpath and system server classpath.

Also, update ArtManager API to use Executors instead of Handlers.

(cherry picked from commit fcbb74a4)

Test: gts GtsAndroidRuntimeManagerHostTestCases
Bug: 30934496
Merged-In: Ie501947a659d644acbde04fb46157dd0c7944e81
Change-Id: Ie501947a659d644acbde04fb46157dd0c7944e81
parent ea6c0ffb
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -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
+62 −47
Original line number Diff line number Diff line
@@ -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 the {@code executor} using the given {@code callback}.
     * The profile will be available as a read-only {@link android.os.ParcelFileDescriptor}.
     *
     * 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}.
     * This method will throw {@link IllegalStateException} if
     * {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given
     * {@code profileType}.
     *
     * @param packageName the target package name
     * @param codePath the code path for which the profile should be retrieved
     * @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");
    }
}
+26 −12
Original line number Diff line number Diff line
@@ -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}.
     * 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}.
     */
    oneway void snapshotRuntimeProfile(in String packageName,
    oneway void snapshotRuntimeProfile(int profileType, in String packageName,
        in String codePath, in ISnapshotRuntimeProfileCallback callback);

     /**
     * Returns true if runtime profiles are enabled, false otherwise.
       * 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}.
       *
     * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission.
       * @param profileType
       */
    boolean isRuntimeProfilingEnabled();
    boolean isRuntimeProfilingEnabled(int profileType);
}
+3 −3
Original line number Diff line number Diff line
@@ -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);
        }
+80 −21
Original line number Diff line number Diff line
@@ -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();

        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);
    }

    /**