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