blob: 9948a3ad47da3f530af3166ca7ac5d0a2163dc36 [file] [log] [blame]
/*
* Copyright (C) 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 com.android.server.pm.dex;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
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.ArtManagerInternal;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
import android.content.pm.dex.PackageOptimizationInfo;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.system.Os;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.RoSystemProperties;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PackageManagerServiceCompilerMapping;
import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
import java.io.File;
import java.io.FileNotFoundException;
/**
* A system service that provides access to runtime and compiler artifacts.
*
* This service is not accessed by users directly, instead one uses an instance of
* {@link ArtManager}, which can be accessed via {@link PackageManager} as follows:
* <p/>
* {@code context().getPackageManager().getArtManager();}
* <p class="note">
* Note: Accessing runtime artifacts may require extra permissions. For example querying the
* runtime profiles of apps requires {@link android.Manifest.permission#READ_RUNTIME_PROFILES}
* which is a system-level permission that will not be granted to normal apps.
*/
public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
private static final String TAG = "ArtManagerService";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// 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 Context mContext;
private final IPackageManager mPackageManager;
private final Object mInstallLock;
@GuardedBy("mInstallLock")
private final Installer mInstaller;
private final Handler mHandler;
static {
verifyTronLoggingConstants();
}
public ArtManagerService(Context context, IPackageManager pm, Installer installer,
Object installLock) {
mContext = context;
mPackageManager = pm;
mInstaller = installer;
mInstallLock = installLock;
mHandler = new Handler(BackgroundThread.getHandler().getLooper());
LocalServices.addService(ArtManagerInternal.class, new ArtManagerInternalImpl());
}
private boolean checkAndroidPermissions(int callingUid, String callingPackage) {
// Callers always need this permission
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_RUNTIME_PROFILES, TAG);
// Callers also need the ability to read usage statistics
switch (mContext.getSystemService(AppOpsManager.class)
.noteOp(AppOpsManager.OP_GET_USAGE_STATS, callingUid, callingPackage)) {
case AppOpsManager.MODE_ALLOWED:
return true;
case AppOpsManager.MODE_DEFAULT:
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.PACKAGE_USAGE_STATS, TAG);
return true;
default:
return false;
}
}
/**
* Checks if the calling user is the shell user and if it is, it checks if it can
* to take a profile snapshot of the give package:
* - on debuggable builds the shell user can take profile snapshots of any app.
* - on non-debuggable builds the shell user can only take snapshots of debuggable apps.
*
* Returns true iff the callingUid is the shell uid and the shell is allowed snapshot profiles.
*
* Note that the root users will go through the regular {@link #checkAndroidPermissions) checks.
*/
private boolean checkShellPermissions(@ProfileType int profileType, String packageName,
int callingUid) {
if (callingUid != Process.SHELL_UID) {
return false;
}
if (RoSystemProperties.DEBUGGABLE) {
return true;
}
if (profileType == ArtManager.PROFILE_BOOT_IMAGE) {
// The shell cannot profile the boot image on non-debuggable builds.
return false;
}
PackageInfo info = null;
try {
info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0);
} catch (RemoteException ignored) {
// Should not happen.
}
if (info == null) {
return false;
}
// On user builds the shell can only profile debuggable apps.
return (info.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE)
== ApplicationInfo.FLAG_DEBUGGABLE;
}
@Override
public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
@Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback,
String callingPackage) {
int callingUid = Binder.getCallingUid();
if (!checkShellPermissions(profileType, packageName, callingUid) &&
!checkAndroidPermissions(callingUid, callingPackage)) {
try {
callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
} catch (RemoteException ignored) {
}
return;
}
// Sanity checks on the arguments.
Preconditions.checkNotNull(callback);
boolean bootImageProfile = profileType == ArtManager.PROFILE_BOOT_IMAGE;
if (!bootImageProfile) {
Preconditions.checkStringNotEmpty(codePath);
Preconditions.checkStringNotEmpty(packageName);
}
// Verify that runtime profiling is enabled.
if (!isRuntimeProfilingEnabled(profileType, callingPackage)) {
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.
// This doesn't really matter because for user 0 we always get a package back (even if
// it's not installed for the user 0). It is ok because we only care about the code
// paths and not if the package is enabled or not for the user.
// TODO(calin): consider adding an API to PMS which can retrieve the
// PackageParser.Package.
info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0);
} catch (RemoteException ignored) {
// Should not happen.
}
if (info == null) {
postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND);
return;
}
boolean pathFound = info.applicationInfo.getBaseCodePath().equals(codePath);
String splitName = null;
String[] splitCodePaths = info.applicationInfo.getSplitCodePaths();
if (!pathFound && (splitCodePaths != null)) {
for (int i = splitCodePaths.length - 1; i >= 0; i--) {
if (splitCodePaths[i].equals(codePath)) {
pathFound = true;
splitName = info.applicationInfo.splitNames[i];
break;
}
}
}
if (!pathFound) {
postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND);
return;
}
// All good, create the profile snapshot.
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, ArtManager.getProfileName(splitName));
}
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(appId, packageName, profileName, classpath)) {
postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
return;
}
} catch (InstallerException e) {
postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
return;
}
}
// Open the snapshot and invoke the callback.
File snapshotProfile = ArtManager.getProfileSnapshotFileForName(packageName, profileName);
ParcelFileDescriptor fd = null;
try {
fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY);
if (fd == null || !fd.getFileDescriptor().valid()) {
Slog.wtf(TAG,
"ParcelFileDescriptor.open returned an invalid descriptor for "
+ packageName + ":" + snapshotProfile + ". isNull=" + (fd == null));
postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
} else {
postSuccess(packageName, fd, callback);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":"
+ snapshotProfile, e);
postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
}
}
private void destroyProfileSnapshot(String packageName, String profileName) {
if (DEBUG) {
Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + profileName);
}
synchronized (mInstallLock) {
try {
mInstaller.destroyProfileSnapshot(packageName, profileName);
} catch (InstallerException e) {
Slog.e(TAG, "Failed to destroy profile snapshot for " +
packageName + ":" + profileName, e);
}
}
}
@Override
public boolean isRuntimeProfilingEnabled(@ProfileType int profileType, String callingPackage) {
int callingUid = Binder.getCallingUid();
if (callingUid != Process.SHELL_UID && !checkAndroidPermissions(callingUid, callingPackage)) {
return false;
}
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);
}
/**
* Post {@link ISnapshotRuntimeProfileCallback#onError(int)} with the given error message
* on the internal {@code mHandler}.
*/
private void postError(ISnapshotRuntimeProfileCallback callback, String packageName,
int errCode) {
if (DEBUG) {
Slog.d(TAG, "Failed to snapshot profile for " + packageName + " with error: " +
errCode);
}
mHandler.post(() -> {
try {
callback.onError(errCode);
} catch (Exception e) {
Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e);
}
});
}
private void postSuccess(String packageName, ParcelFileDescriptor fd,
ISnapshotRuntimeProfileCallback callback) {
if (DEBUG) {
Slog.d(TAG, "Successfully snapshot profile for " + packageName);
}
mHandler.post(() -> {
try {
// Double check that the descriptor is still valid.
// We've seen production issues (b/76028139) where this can turn invalid (there are
// suspicions around the finalizer behaviour).
if (fd.getFileDescriptor().valid()) {
callback.onSuccess(fd);
} else {
Slog.wtf(TAG, "The snapshot FD became invalid before posting the result for "
+ packageName);
callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
}
} catch (Exception e) {
Slog.w(TAG,
"Failed to call onSuccess after profile snapshot for " + packageName, e);
} finally {
IoUtils.closeQuietly(fd);
}
});
}
/**
* Prepare the application profiles.
* For all code paths:
* - create the current primary profile to save time at app startup time.
* - copy the profiles from the associated dex metadata file to the reference profile.
*/
public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user,
boolean updateReferenceProfileContent) {
final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
if (user < 0) {
Slog.wtf(TAG, "Invalid user id: " + user);
return;
}
if (appId < 0) {
Slog.wtf(TAG, "Invalid app id: " + appId);
return;
}
try {
ArrayMap<String, String> codePathsProfileNames = getPackageProfileNames(pkg);
for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) {
String codePath = codePathsProfileNames.keyAt(i);
String profileName = codePathsProfileNames.valueAt(i);
String dexMetadataPath = null;
// Passing the dex metadata file to the prepare method will update the reference
// profile content. As such, we look for the dex metadata file only if we need to
// perform an update.
if (updateReferenceProfileContent) {
File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath));
dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();
}
synchronized (mInstaller) {
boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId,
profileName, codePath, dexMetadataPath);
if (!result) {
Slog.e(TAG, "Failed to prepare profile for " +
pkg.packageName + ":" + codePath);
}
}
}
} catch (InstallerException e) {
Slog.e(TAG, "Failed to prepare profile for " + pkg.packageName, e);
}
}
/**
* Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}.
*/
public void prepareAppProfiles(PackageParser.Package pkg, int[] user,
boolean updateReferenceProfileContent) {
for (int i = 0; i < user.length; i++) {
prepareAppProfiles(pkg, user[i], updateReferenceProfileContent);
}
}
/**
* Clear the profiles for the given package.
*/
public void clearAppProfiles(PackageParser.Package pkg) {
try {
ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
String profileName = packageProfileNames.valueAt(i);
mInstaller.clearAppProfiles(pkg.packageName, profileName);
}
} catch (InstallerException e) {
Slog.w(TAG, String.valueOf(e));
}
}
/**
* Dumps the profiles for the given package.
*/
public void dumpProfiles(PackageParser.Package pkg) {
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
try {
ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
String codePath = packageProfileNames.keyAt(i);
String profileName = packageProfileNames.valueAt(i);
synchronized (mInstallLock) {
mInstaller.dumpProfiles(sharedGid, pkg.packageName, profileName, codePath);
}
}
} catch (InstallerException e) {
Slog.w(TAG, "Failed to dump profiles", e);
}
}
/**
* Compile layout resources in a given package.
*/
public boolean compileLayouts(PackageParser.Package pkg) {
try {
final String packageName = pkg.packageName;
final String apkPath = pkg.baseCodePath;
final ApplicationInfo appInfo = pkg.applicationInfo;
final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex";
if (appInfo.isPrivilegedApp() || appInfo.isEmbeddedDexUsed()
|| appInfo.isDefaultToDeviceProtectedStorage()) {
// Privileged apps prefer to load trusted code so they don't use compiled views.
// If the app is not privileged but prefers code integrity, also avoid compiling
// views.
// Also disable the view compiler for protected storage apps since there are
// selinux permissions required for writing to user_de.
return false;
}
Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
") to " + outDexFile);
long callingId = Binder.clearCallingIdentity();
try {
synchronized (mInstallLock) {
return mInstaller.compileLayouts(apkPath, packageName, outDexFile,
appInfo.uid);
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
catch (Throwable e) {
Log.e("PackageManager", "Failed to compile layouts", e);
return false;
}
}
/**
* Build the profiles names for all the package code paths (excluding resource only paths).
* Return the map [code path -> profile name].
*/
private ArrayMap<String, String> getPackageProfileNames(PackageParser.Package pkg) {
ArrayMap<String, String> result = new ArrayMap<>();
if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
result.put(pkg.baseCodePath, ArtManager.getProfileName(null));
}
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (int i = 0; i < pkg.splitCodePaths.length; i++) {
if ((pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
result.put(pkg.splitCodePaths[i], ArtManager.getProfileName(pkg.splitNames[i]));
}
}
}
return result;
}
// Constants used for logging compilation filter to TRON.
// DO NOT CHANGE existing values.
//
// NOTE: '-1' value is reserved for the case where we cannot produce a valid
// PackageOptimizationInfo because the ArtManagerInternal is not ready to be used by the
// ActivityMetricsLoggers.
private static final int TRON_COMPILATION_FILTER_ERROR = 0;
private static final int TRON_COMPILATION_FILTER_UNKNOWN = 1;
private static final int TRON_COMPILATION_FILTER_ASSUMED_VERIFIED = 2;
private static final int TRON_COMPILATION_FILTER_EXTRACT = 3;
private static final int TRON_COMPILATION_FILTER_VERIFY = 4;
private static final int TRON_COMPILATION_FILTER_QUICKEN = 5;
private static final int TRON_COMPILATION_FILTER_SPACE_PROFILE = 6;
private static final int TRON_COMPILATION_FILTER_SPACE = 7;
private static final int TRON_COMPILATION_FILTER_SPEED_PROFILE = 8;
private static final int TRON_COMPILATION_FILTER_SPEED = 9;
private static final int TRON_COMPILATION_FILTER_EVERYTHING_PROFILE = 10;
private static final int TRON_COMPILATION_FILTER_EVERYTHING = 11;
private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK = 12;
private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK = 13;
private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK = 14;
// Constants used for logging compilation reason to TRON.
// DO NOT CHANGE existing values.
//
// NOTE: '-1' value is reserved for the case where we cannot produce a valid
// PackageOptimizationInfo because the ArtManagerInternal is not ready to be used by the
// ActivityMetricsLoggers.
private static final int TRON_COMPILATION_REASON_ERROR = 0;
private static final int TRON_COMPILATION_REASON_UNKNOWN = 1;
private static final int TRON_COMPILATION_REASON_FIRST_BOOT = 2;
private static final int TRON_COMPILATION_REASON_BOOT = 3;
private static final int TRON_COMPILATION_REASON_INSTALL = 4;
private static final int TRON_COMPILATION_REASON_BG_DEXOPT = 5;
private static final int TRON_COMPILATION_REASON_AB_OTA = 6;
private static final int TRON_COMPILATION_REASON_INACTIVE = 7;
private static final int TRON_COMPILATION_REASON_SHARED = 8;
private static final int TRON_COMPILATION_REASON_INSTALL_WITH_DEX_METADATA = 9;
// The annotation to add as a suffix to the compilation reason when dexopt was
// performed with dex metadata.
public static final String DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION = "-dm";
/**
* Convert the compilation reason to an int suitable to be logged to TRON.
*/
private static int getCompilationReasonTronValue(String compilationReason) {
switch (compilationReason) {
case "unknown" : return TRON_COMPILATION_REASON_UNKNOWN;
case "error" : return TRON_COMPILATION_REASON_ERROR;
case "first-boot" : return TRON_COMPILATION_REASON_FIRST_BOOT;
case "boot" : return TRON_COMPILATION_REASON_BOOT;
case "install" : return TRON_COMPILATION_REASON_INSTALL;
case "bg-dexopt" : return TRON_COMPILATION_REASON_BG_DEXOPT;
case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA;
case "inactive" : return TRON_COMPILATION_REASON_INACTIVE;
case "shared" : return TRON_COMPILATION_REASON_SHARED;
// This is a special marker for dex metadata installation that does not
// have an equivalent as a system property.
case "install" + DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION :
return TRON_COMPILATION_REASON_INSTALL_WITH_DEX_METADATA;
default: return TRON_COMPILATION_REASON_UNKNOWN;
}
}
/**
* Convert the compilation filter to an int suitable to be logged to TRON.
*/
private static int getCompilationFilterTronValue(String compilationFilter) {
switch (compilationFilter) {
case "error" : return TRON_COMPILATION_FILTER_ERROR;
case "unknown" : return TRON_COMPILATION_FILTER_UNKNOWN;
case "assume-verified" : return TRON_COMPILATION_FILTER_ASSUMED_VERIFIED;
case "extract" : return TRON_COMPILATION_FILTER_EXTRACT;
case "verify" : return TRON_COMPILATION_FILTER_VERIFY;
case "quicken" : return TRON_COMPILATION_FILTER_QUICKEN;
case "space-profile" : return TRON_COMPILATION_FILTER_SPACE_PROFILE;
case "space" : return TRON_COMPILATION_FILTER_SPACE;
case "speed-profile" : return TRON_COMPILATION_FILTER_SPEED_PROFILE;
case "speed" : return TRON_COMPILATION_FILTER_SPEED;
case "everything-profile" : return TRON_COMPILATION_FILTER_EVERYTHING_PROFILE;
case "everything" : return TRON_COMPILATION_FILTER_EVERYTHING;
case "run-from-apk" : return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK;
case "run-from-apk-fallback" :
return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
case "run-from-vdex-fallback" :
return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
default: return TRON_COMPILATION_FILTER_UNKNOWN;
}
}
private static void verifyTronLoggingConstants() {
for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
String reason = PackageManagerServiceCompilerMapping.REASON_STRINGS[i];
int value = getCompilationReasonTronValue(reason);
if (value == TRON_COMPILATION_REASON_ERROR
|| value == TRON_COMPILATION_REASON_UNKNOWN) {
throw new IllegalArgumentException("Compilation reason not configured for TRON "
+ "logging: " + reason);
}
}
}
private class ArtManagerInternalImpl extends ArtManagerInternal {
@Override
public PackageOptimizationInfo getPackageOptimizationInfo(
ApplicationInfo info, String abi) {
String compilationReason;
String compilationFilter;
try {
String isa = VMRuntime.getInstructionSet(abi);
DexFile.OptimizationInfo optInfo =
DexFile.getDexFileOptimizationInfo(info.getBaseCodePath(), isa);
compilationFilter = optInfo.getStatus();
compilationReason = optInfo.getReason();
} catch (FileNotFoundException e) {
Slog.e(TAG, "Could not get optimizations status for " + info.getBaseCodePath(), e);
compilationFilter = "error";
compilationReason = "error";
} catch (IllegalArgumentException e) {
Slog.wtf(TAG, "Requested optimization status for " + info.getBaseCodePath()
+ " due to an invalid abi " + abi, e);
compilationFilter = "error";
compilationReason = "error";
}
int compilationFilterTronValue = getCompilationFilterTronValue(compilationFilter);
int compilationReasonTronValue = getCompilationReasonTronValue(compilationReason);
return new PackageOptimizationInfo(
compilationFilterTronValue, compilationReasonTronValue);
}
}
}