| /** |
| * Copyright 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| 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.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 |
| * packages that are currently installed on the device. |
| * |
| * @hide |
| */ |
| @SystemApi |
| public class ArtManager { |
| private static final String TAG = "ArtManager"; |
| |
| /** The snapshot failed because the package was not found. */ |
| public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0; |
| /** The snapshot failed because the package code path does not exist. */ |
| public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1; |
| /** 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; |
| |
| /** |
| * @hide |
| */ |
| public ArtManager(@NonNull IArtManager manager) { |
| mArtManager = manager; |
| } |
| |
| /** |
| * 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}. |
| * |
| * 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 executor the executor which should be used to post the result |
| */ |
| @RequiresPermission(android.Manifest.permission.READ_RUNTIME_PROFILES) |
| 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, executor); |
| try { |
| mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate); |
| } catch (RemoteException e) { |
| e.rethrowAsRuntimeException(); |
| } |
| } |
| |
| /** |
| * 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(@ProfileType int profileType) { |
| try { |
| return mArtManager.isRuntimeProfilingEnabled(profileType); |
| } catch (RemoteException e) { |
| e.rethrowAsRuntimeException(); |
| } |
| return false; |
| } |
| |
| /** |
| * Callback used for retrieving runtime profiles. |
| */ |
| public abstract static class SnapshotRuntimeProfileCallback { |
| /** |
| * Called when the profile snapshot finished with success. |
| * |
| * @param profileReadFd the file descriptor that can be used to read the profile. Note that |
| * the file might be empty (which is valid profile). |
| */ |
| public abstract void onSuccess(ParcelFileDescriptor profileReadFd); |
| |
| /** |
| * Called when the profile snapshot finished with an error. |
| * |
| * @param errCode the error code {@see SNAPSHOT_FAILED_PACKAGE_NOT_FOUND, |
| * SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND, SNAPSHOT_FAILED_INTERNAL_ERROR}. |
| */ |
| public abstract void onError(int errCode); |
| } |
| |
| private static class SnapshotRuntimeProfileCallbackDelegate |
| extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub { |
| private final ArtManager.SnapshotRuntimeProfileCallback mCallback; |
| private final Executor mExecutor; |
| |
| private SnapshotRuntimeProfileCallbackDelegate( |
| ArtManager.SnapshotRuntimeProfileCallback callback, Executor executor) { |
| mCallback = callback; |
| mExecutor = executor; |
| } |
| |
| @Override |
| public void onSuccess(final ParcelFileDescriptor profileReadFd) { |
| mExecutor.execute(() -> mCallback.onSuccess(profileReadFd)); |
| } |
| |
| @Override |
| public void onError(int errCode) { |
| mExecutor.execute(() -> mCallback.onError(errCode)); |
| } |
| } |
| |
| /** |
| * Return the profile name for the given split. If {@code splitName} is null the |
| * method returns the profile name for the base apk. |
| * |
| * @hide |
| */ |
| public static String getProfileName(String splitName) { |
| return splitName == null ? "primary.prof" : splitName + ".split.prof"; |
| } |
| |
| /** |
| * Return the path to the current profile corresponding to given package and split. |
| * |
| * @hide |
| */ |
| public static String getCurrentProfilePath(String packageName, int userId, String splitName) { |
| File profileDir = Environment.getDataProfilesDePackageDirectory(userId, packageName); |
| return new File(profileDir, getProfileName(splitName)).getAbsolutePath(); |
| } |
| |
| /** |
| * 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 getProfileSnapshotFileForName(String packageName, String profileName) { |
| File profileDir = Environment.getDataRefProfilesDePackageDirectory(packageName); |
| return new File(profileDir, profileName + ".snapshot"); |
| } |
| } |