1 /** 2 * Copyright 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.content.pm.dex; 18 19 import static android.Manifest.permission.PACKAGE_USAGE_STATS; 20 import static android.Manifest.permission.READ_RUNTIME_PROFILES; 21 22 import android.annotation.CallbackExecutor; 23 import android.annotation.IntDef; 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.RequiresPermission; 27 import android.annotation.SystemApi; 28 import android.content.Context; 29 import android.os.Environment; 30 import android.os.ParcelFileDescriptor; 31 import android.os.RemoteException; 32 import android.util.Slog; 33 34 import java.io.File; 35 import java.lang.annotation.Retention; 36 import java.lang.annotation.RetentionPolicy; 37 import java.util.concurrent.Executor; 38 39 /** 40 * Class for retrieving various kinds of information related to the runtime artifacts of 41 * packages that are currently installed on the device. 42 * 43 * @hide 44 */ 45 @SystemApi 46 public class ArtManager { 47 private static final String TAG = "ArtManager"; 48 49 /** The snapshot failed because the package was not found. */ 50 public static final int SNAPSHOT_FAILED_PACKAGE_NOT_FOUND = 0; 51 /** The snapshot failed because the package code path does not exist. */ 52 public static final int SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND = 1; 53 /** The snapshot failed because of an internal error (e.g. error during opening profiles). */ 54 public static final int SNAPSHOT_FAILED_INTERNAL_ERROR = 2; 55 56 /** Constant used for applications profiles. */ 57 public static final int PROFILE_APPS = 0; 58 /** Constant used for the boot image profile. */ 59 public static final int PROFILE_BOOT_IMAGE = 1; 60 61 /** @hide */ 62 @IntDef(flag = true, prefix = { "PROFILE_" }, value = { 63 PROFILE_APPS, 64 PROFILE_BOOT_IMAGE, 65 }) 66 @Retention(RetentionPolicy.SOURCE) 67 public @interface ProfileType {} 68 69 private final Context mContext; 70 private final IArtManager mArtManager; 71 72 /** 73 * @hide 74 */ ArtManager(@onNull Context context, @NonNull IArtManager manager)75 public ArtManager(@NonNull Context context, @NonNull IArtManager manager) { 76 mContext = context; 77 mArtManager = manager; 78 } 79 80 /** 81 * Snapshots a runtime profile according to the {@code profileType} parameter. 82 * 83 * If {@code profileType} is {@link ArtManager#PROFILE_APPS} the method will snapshot 84 * the profile for for an apk belonging to the package {@code packageName}. 85 * The apk is identified by {@code codePath}. 86 * 87 * If {@code profileType} is {@code ArtManager.PROFILE_BOOT_IMAGE} the method will snapshot 88 * the profile for the boot image. In this case {@code codePath can be null}. The parameters 89 * {@code packageName} and {@code codePath} are ignored. 90 *u 91 * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission. 92 * 93 * The result will be posted on the {@code executor} using the given {@code callback}. 94 * The profile will be available as a read-only {@link android.os.ParcelFileDescriptor}. 95 * 96 * This method will throw {@link IllegalStateException} if 97 * {@link ArtManager#isRuntimeProfilingEnabled(int)} does not return true for the given 98 * {@code profileType}. 99 * 100 * @param profileType the type of profile that should be snapshot (boot image or app) 101 * @param packageName the target package name or null if the target is the boot image 102 * @param codePath the code path for which the profile should be retrieved or null if 103 * the target is the boot image 104 * @param callback the callback which should be used for the result 105 * @param executor the executor which should be used to post the result 106 */ 107 @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS }) snapshotRuntimeProfile(@rofileType int profileType, @Nullable String packageName, @Nullable String codePath, @NonNull @CallbackExecutor Executor executor, @NonNull SnapshotRuntimeProfileCallback callback)108 public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName, 109 @Nullable String codePath, @NonNull @CallbackExecutor Executor executor, 110 @NonNull SnapshotRuntimeProfileCallback callback) { 111 Slog.d(TAG, "Requesting profile snapshot for " + packageName + ":" + codePath); 112 113 SnapshotRuntimeProfileCallbackDelegate delegate = 114 new SnapshotRuntimeProfileCallbackDelegate(callback, executor); 115 try { 116 mArtManager.snapshotRuntimeProfile(profileType, packageName, codePath, delegate, 117 mContext.getOpPackageName()); 118 } catch (RemoteException e) { 119 throw e.rethrowAsRuntimeException(); 120 } 121 } 122 123 /** 124 * Returns true if runtime profiles are enabled for the given type, false otherwise. 125 * 126 * The calling process must have {@code android.permission.READ_RUNTIME_PROFILE} permission. 127 * 128 * @param profileType can be either {@link ArtManager#PROFILE_APPS} 129 * or {@link ArtManager#PROFILE_BOOT_IMAGE} 130 */ 131 @RequiresPermission(allOf = { READ_RUNTIME_PROFILES, PACKAGE_USAGE_STATS }) isRuntimeProfilingEnabled(@rofileType int profileType)132 public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) { 133 try { 134 return mArtManager.isRuntimeProfilingEnabled(profileType, mContext.getOpPackageName()); 135 } catch (RemoteException e) { 136 throw e.rethrowAsRuntimeException(); 137 } 138 } 139 140 /** 141 * Callback used for retrieving runtime profiles. 142 */ 143 public abstract static class SnapshotRuntimeProfileCallback { 144 /** 145 * Called when the profile snapshot finished with success. 146 * 147 * @param profileReadFd the file descriptor that can be used to read the profile. Note that 148 * the file might be empty (which is valid profile). 149 */ onSuccess(ParcelFileDescriptor profileReadFd)150 public abstract void onSuccess(ParcelFileDescriptor profileReadFd); 151 152 /** 153 * Called when the profile snapshot finished with an error. 154 * 155 * @param errCode the error code {@see SNAPSHOT_FAILED_PACKAGE_NOT_FOUND, 156 * SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND, SNAPSHOT_FAILED_INTERNAL_ERROR}. 157 */ onError(int errCode)158 public abstract void onError(int errCode); 159 } 160 161 private static class SnapshotRuntimeProfileCallbackDelegate 162 extends android.content.pm.dex.ISnapshotRuntimeProfileCallback.Stub { 163 private final ArtManager.SnapshotRuntimeProfileCallback mCallback; 164 private final Executor mExecutor; 165 SnapshotRuntimeProfileCallbackDelegate( ArtManager.SnapshotRuntimeProfileCallback callback, Executor executor)166 private SnapshotRuntimeProfileCallbackDelegate( 167 ArtManager.SnapshotRuntimeProfileCallback callback, Executor executor) { 168 mCallback = callback; 169 mExecutor = executor; 170 } 171 172 @Override onSuccess(final ParcelFileDescriptor profileReadFd)173 public void onSuccess(final ParcelFileDescriptor profileReadFd) { 174 mExecutor.execute(() -> mCallback.onSuccess(profileReadFd)); 175 } 176 177 @Override onError(int errCode)178 public void onError(int errCode) { 179 mExecutor.execute(() -> mCallback.onError(errCode)); 180 } 181 } 182 183 /** 184 * Return the profile name for the given split. If {@code splitName} is null the 185 * method returns the profile name for the base apk. 186 * 187 * @hide 188 */ getProfileName(String splitName)189 public static String getProfileName(String splitName) { 190 return splitName == null ? "primary.prof" : splitName + ".split.prof"; 191 } 192 193 /** 194 * Return the path to the current profile corresponding to given package and split. 195 * 196 * @hide 197 */ getCurrentProfilePath(String packageName, int userId, String splitName)198 public static String getCurrentProfilePath(String packageName, int userId, String splitName) { 199 File profileDir = Environment.getDataProfilesDePackageDirectory(userId, packageName); 200 return new File(profileDir, getProfileName(splitName)).getAbsolutePath(); 201 } 202 203 /** 204 * Return the snapshot profile file for the given package and profile name. 205 * 206 * KEEP in sync with installd dexopt.cpp. 207 * TODO(calin): inject the snapshot profile name from PM to avoid the dependency. 208 * 209 * @hide 210 */ getProfileSnapshotFileForName(String packageName, String profileName)211 public static File getProfileSnapshotFileForName(String packageName, String profileName) { 212 File profileDir = Environment.getDataRefProfilesDePackageDirectory(packageName); 213 return new File(profileDir, profileName + ".snapshot"); 214 } 215 } 216