ArtManagerService.java revision fcbb74a4296fd808e1058ecebd91fac56582e799
1/* 2 * Copyright (C) 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 17package com.android.server.pm.dex; 18 19import android.Manifest; 20import android.annotation.UserIdInt; 21import android.content.pm.ApplicationInfo; 22import android.content.pm.PackageInfo; 23import android.content.pm.PackageManager; 24import android.content.pm.PackageParser; 25import android.content.pm.dex.ArtManager; 26import android.content.pm.dex.ArtManager.ProfileType; 27import android.content.pm.dex.DexMetadataHelper; 28import android.os.Binder; 29import android.os.Build; 30import android.os.Handler; 31import android.os.ParcelFileDescriptor; 32import android.os.RemoteException; 33import android.content.pm.IPackageManager; 34import android.content.pm.dex.ISnapshotRuntimeProfileCallback; 35import android.os.SystemProperties; 36import android.os.UserHandle; 37import android.system.Os; 38import android.util.ArrayMap; 39import android.util.Slog; 40 41import com.android.internal.annotations.GuardedBy; 42import com.android.internal.os.BackgroundThread; 43import com.android.internal.util.ArrayUtils; 44import com.android.internal.util.Preconditions; 45import com.android.server.pm.Installer; 46import com.android.server.pm.Installer.InstallerException; 47import java.io.File; 48import java.io.FileNotFoundException; 49import libcore.io.IoUtils; 50import libcore.util.NonNull; 51import libcore.util.Nullable; 52 53/** 54 * A system service that provides access to runtime and compiler artifacts. 55 * 56 * This service is not accessed by users directly, instead one uses an instance of 57 * {@link ArtManager}, which can be accessed via {@link PackageManager} as follows: 58 * <p/> 59 * {@code context().getPackageManager().getArtManager();} 60 * <p class="note"> 61 * Note: Accessing runtime artifacts may require extra permissions. For example querying the 62 * runtime profiles of apps requires {@link android.Manifest.permission#READ_RUNTIME_PROFILES} 63 * which is a system-level permission that will not be granted to normal apps. 64 */ 65public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { 66 private static final String TAG = "ArtManagerService"; 67 68 private static boolean DEBUG = false; 69 private static boolean DEBUG_IGNORE_PERMISSIONS = false; 70 71 // Package name used to create the profile directory layout when 72 // taking a snapshot of the boot image profile. 73 private static final String BOOT_IMAGE_ANDROID_PACKAGE = "android"; 74 // Profile name used for the boot image profile. 75 private static final String BOOT_IMAGE_PROFILE_NAME = "android.prof"; 76 77 private final IPackageManager mPackageManager; 78 private final Object mInstallLock; 79 @GuardedBy("mInstallLock") 80 private final Installer mInstaller; 81 82 private final Handler mHandler; 83 84 public ArtManagerService(IPackageManager pm, Installer installer, Object installLock) { 85 mPackageManager = pm; 86 mInstaller = installer; 87 mInstallLock = installLock; 88 mHandler = new Handler(BackgroundThread.getHandler().getLooper()); 89 } 90 91 @Override 92 public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName, 93 @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback) { 94 // Sanity checks on the arguments. 95 Preconditions.checkNotNull(callback); 96 97 boolean bootImageProfile = profileType == ArtManager.PROFILE_BOOT_IMAGE; 98 if (!bootImageProfile) { 99 Preconditions.checkStringNotEmpty(codePath); 100 Preconditions.checkStringNotEmpty(packageName); 101 } 102 103 // Verify that the caller has the right permissions and that the runtime profiling is 104 // enabled. The call to isRuntimePermissions will checkReadRuntimeProfilePermission. 105 if (!isRuntimeProfilingEnabled(profileType)) { 106 throw new IllegalStateException("Runtime profiling is not enabled for " + profileType); 107 } 108 109 if (DEBUG) { 110 Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath); 111 } 112 113 if (bootImageProfile) { 114 snapshotBootImageProfile(callback); 115 } else { 116 snapshotAppProfile(packageName, codePath, callback); 117 } 118 } 119 120 private void snapshotAppProfile(String packageName, String codePath, 121 ISnapshotRuntimeProfileCallback callback) { 122 PackageInfo info = null; 123 try { 124 // Note that we use the default user 0 to retrieve the package info. 125 // This doesn't really matter because for user 0 we always get a package back (even if 126 // it's not installed for the user 0). It is ok because we only care about the code 127 // paths and not if the package is enabled or not for the user. 128 129 // TODO(calin): consider adding an API to PMS which can retrieve the 130 // PackageParser.Package. 131 info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0); 132 } catch (RemoteException ignored) { 133 // Should not happen. 134 } 135 if (info == null) { 136 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND); 137 return; 138 } 139 140 boolean pathFound = info.applicationInfo.getBaseCodePath().equals(codePath); 141 String splitName = null; 142 String[] splitCodePaths = info.applicationInfo.getSplitCodePaths(); 143 if (!pathFound && (splitCodePaths != null)) { 144 for (int i = splitCodePaths.length - 1; i >= 0; i--) { 145 if (splitCodePaths[i].equals(codePath)) { 146 pathFound = true; 147 splitName = info.applicationInfo.splitNames[i]; 148 break; 149 } 150 } 151 } 152 if (!pathFound) { 153 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND); 154 return; 155 } 156 157 // All good, create the profile snapshot. 158 int appId = UserHandle.getAppId(info.applicationInfo.uid); 159 if (appId < 0) { 160 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); 161 Slog.wtf(TAG, "AppId is -1 for package: " + packageName); 162 return; 163 } 164 165 createProfileSnapshot(packageName, ArtManager.getProfileName(splitName), codePath, 166 appId, callback); 167 // Destroy the snapshot, we no longer need it. 168 destroyProfileSnapshot(packageName, ArtManager.getProfileName(splitName)); 169 } 170 171 private void createProfileSnapshot(String packageName, String profileName, String classpath, 172 int appId, ISnapshotRuntimeProfileCallback callback) { 173 // Ask the installer to snapshot the profile. 174 synchronized (mInstallLock) { 175 try { 176 if (!mInstaller.createProfileSnapshot(appId, packageName, profileName, classpath)) { 177 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); 178 return; 179 } 180 } catch (InstallerException e) { 181 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); 182 return; 183 } 184 } 185 186 // Open the snapshot and invoke the callback. 187 File snapshotProfile = ArtManager.getProfileSnapshotFileForName(packageName, profileName); 188 189 ParcelFileDescriptor fd = null; 190 try { 191 fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY); 192 postSuccess(packageName, fd, callback); 193 } catch (FileNotFoundException e) { 194 Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":" 195 + snapshotProfile, e); 196 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR); 197 } finally { 198 IoUtils.closeQuietly(fd); 199 } 200 } 201 202 private void destroyProfileSnapshot(String packageName, String profileName) { 203 if (DEBUG) { 204 Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + profileName); 205 } 206 207 synchronized (mInstallLock) { 208 try { 209 mInstaller.destroyProfileSnapshot(packageName, profileName); 210 } catch (InstallerException e) { 211 Slog.e(TAG, "Failed to destroy profile snapshot for " + 212 packageName + ":" + profileName, e); 213 } 214 } 215 } 216 217 @Override 218 public boolean isRuntimeProfilingEnabled(@ProfileType int profileType) { 219 // Verify that the caller has the right permissions. 220 checkReadRuntimeProfilePermission(); 221 222 switch (profileType) { 223 case ArtManager.PROFILE_APPS : 224 return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false); 225 case ArtManager.PROFILE_BOOT_IMAGE: 226 return (Build.IS_USERDEBUG || Build.IS_ENG) && 227 SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false) && 228 SystemProperties.getBoolean("dalvik.vm.profilebootimage", false); 229 default: 230 throw new IllegalArgumentException("Invalid profile type:" + profileType); 231 } 232 } 233 234 private void snapshotBootImageProfile(ISnapshotRuntimeProfileCallback callback) { 235 // Combine the profiles for boot classpath and system server classpath. 236 // This avoids having yet another type of profiles and simplifies the processing. 237 String classpath = String.join(":", Os.getenv("BOOTCLASSPATH"), 238 Os.getenv("SYSTEMSERVERCLASSPATH")); 239 240 // Create the snapshot. 241 createProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME, classpath, 242 /*appId*/ -1, callback); 243 // Destroy the snapshot, we no longer need it. 244 destroyProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME); 245 } 246 247 /** 248 * Post {@link ISnapshotRuntimeProfileCallback#onError(int)} with the given error message 249 * on the internal {@code mHandler}. 250 */ 251 private void postError(ISnapshotRuntimeProfileCallback callback, String packageName, 252 int errCode) { 253 if (DEBUG) { 254 Slog.d(TAG, "Failed to snapshot profile for " + packageName + " with error: " + 255 errCode); 256 } 257 mHandler.post(() -> { 258 try { 259 callback.onError(errCode); 260 } catch (RemoteException e) { 261 Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e); 262 } 263 }); 264 } 265 266 private void postSuccess(String packageName, ParcelFileDescriptor fd, 267 ISnapshotRuntimeProfileCallback callback) { 268 if (DEBUG) { 269 Slog.d(TAG, "Successfully snapshot profile for " + packageName); 270 } 271 mHandler.post(() -> { 272 try { 273 callback.onSuccess(fd); 274 } catch (RemoteException e) { 275 Slog.w(TAG, 276 "Failed to call onSuccess after profile snapshot for " + packageName, e); 277 } 278 }); 279 } 280 281 /** 282 * Verify that the binder calling uid has {@code android.permission.READ_RUNTIME_PROFILE}. 283 * If not, it throws a {@link SecurityException}. 284 */ 285 private void checkReadRuntimeProfilePermission() { 286 if (DEBUG_IGNORE_PERMISSIONS) { 287 return; 288 } 289 try { 290 int result = mPackageManager.checkUidPermission( 291 Manifest.permission.READ_RUNTIME_PROFILES, Binder.getCallingUid()); 292 if (result != PackageManager.PERMISSION_GRANTED) { 293 throw new SecurityException("You need " 294 + Manifest.permission.READ_RUNTIME_PROFILES 295 + " permission to snapshot profiles."); 296 } 297 } catch (RemoteException e) { 298 // Should not happen. 299 } 300 } 301 302 /** 303 * Prepare the application profiles. 304 * For all code paths: 305 * - create the current primary profile to save time at app startup time. 306 * - copy the profiles from the associated dex metadata file to the reference profile. 307 */ 308 public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user) { 309 final int appId = UserHandle.getAppId(pkg.applicationInfo.uid); 310 if (user < 0) { 311 Slog.wtf(TAG, "Invalid user id: " + user); 312 return; 313 } 314 if (appId < 0) { 315 Slog.wtf(TAG, "Invalid app id: " + appId); 316 return; 317 } 318 try { 319 ArrayMap<String, String> codePathsProfileNames = getPackageProfileNames(pkg); 320 for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) { 321 String codePath = codePathsProfileNames.keyAt(i); 322 String profileName = codePathsProfileNames.valueAt(i); 323 File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath)); 324 String dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath(); 325 synchronized (mInstaller) { 326 boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId, 327 profileName, codePath, dexMetadataPath); 328 if (!result) { 329 Slog.e(TAG, "Failed to prepare profile for " + 330 pkg.packageName + ":" + codePath); 331 } 332 } 333 } 334 } catch (InstallerException e) { 335 Slog.e(TAG, "Failed to prepare profile for " + pkg.packageName, e); 336 } 337 } 338 339 /** 340 * Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}. 341 */ 342 public void prepareAppProfiles(PackageParser.Package pkg, int[] user) { 343 for (int i = 0; i < user.length; i++) { 344 prepareAppProfiles(pkg, user[i]); 345 } 346 } 347 348 /** 349 * Clear the profiles for the given package. 350 */ 351 public void clearAppProfiles(PackageParser.Package pkg) { 352 try { 353 ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg); 354 for (int i = packageProfileNames.size() - 1; i >= 0; i--) { 355 String profileName = packageProfileNames.valueAt(i); 356 mInstaller.clearAppProfiles(pkg.packageName, profileName); 357 } 358 } catch (InstallerException e) { 359 Slog.w(TAG, String.valueOf(e)); 360 } 361 } 362 363 /** 364 * Dumps the profiles for the given package. 365 */ 366 public void dumpProfiles(PackageParser.Package pkg) { 367 final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); 368 try { 369 ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg); 370 for (int i = packageProfileNames.size() - 1; i >= 0; i--) { 371 String codePath = packageProfileNames.keyAt(i); 372 String profileName = packageProfileNames.valueAt(i); 373 synchronized (mInstallLock) { 374 mInstaller.dumpProfiles(sharedGid, pkg.packageName, profileName, codePath); 375 } 376 } 377 } catch (InstallerException e) { 378 Slog.w(TAG, "Failed to dump profiles", e); 379 } 380 } 381 382 /** 383 * Build the profiles names for all the package code paths (excluding resource only paths). 384 * Return the map [code path -> profile name]. 385 */ 386 private ArrayMap<String, String> getPackageProfileNames(PackageParser.Package pkg) { 387 ArrayMap<String, String> result = new ArrayMap<>(); 388 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) { 389 result.put(pkg.baseCodePath, ArtManager.getProfileName(null)); 390 } 391 if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) { 392 for (int i = 0; i < pkg.splitCodePaths.length; i++) { 393 if ((pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) { 394 result.put(pkg.splitCodePaths[i], ArtManager.getProfileName(pkg.splitNames[i])); 395 } 396 } 397 } 398 return result; 399 } 400} 401