• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.pm.dex;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.UserIdInt;
22 import android.app.AppOpsManager;
23 import android.content.Context;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.IPackageManager;
26 import android.content.pm.PackageInfo;
27 import android.content.pm.PackageManager;
28 import android.content.pm.PackageParser;
29 import android.content.pm.dex.ArtManager;
30 import android.content.pm.dex.ArtManager.ProfileType;
31 import android.content.pm.dex.ArtManagerInternal;
32 import android.content.pm.dex.DexMetadataHelper;
33 import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
34 import android.content.pm.dex.PackageOptimizationInfo;
35 import android.os.Binder;
36 import android.os.Build;
37 import android.os.Handler;
38 import android.os.ParcelFileDescriptor;
39 import android.os.Process;
40 import android.os.RemoteException;
41 import android.os.SystemProperties;
42 import android.os.UserHandle;
43 import android.system.Os;
44 import android.util.ArrayMap;
45 import android.util.Log;
46 import android.util.Slog;
47 
48 import com.android.internal.annotations.GuardedBy;
49 import com.android.internal.os.BackgroundThread;
50 import com.android.internal.os.RoSystemProperties;
51 import com.android.internal.util.ArrayUtils;
52 import com.android.internal.util.Preconditions;
53 import com.android.server.LocalServices;
54 import com.android.server.pm.Installer;
55 import com.android.server.pm.Installer.InstallerException;
56 import com.android.server.pm.PackageManagerServiceCompilerMapping;
57 
58 import dalvik.system.DexFile;
59 import dalvik.system.VMRuntime;
60 
61 import libcore.io.IoUtils;
62 
63 import java.io.File;
64 import java.io.FileNotFoundException;
65 
66 /**
67  * A system service that provides access to runtime and compiler artifacts.
68  *
69  * This service is not accessed by users directly, instead one uses an instance of
70  * {@link ArtManager}, which can be accessed via {@link PackageManager} as follows:
71  * <p/>
72  * {@code context().getPackageManager().getArtManager();}
73  * <p class="note">
74  * Note: Accessing runtime artifacts may require extra permissions. For example querying the
75  * runtime profiles of apps requires {@link android.Manifest.permission#READ_RUNTIME_PROFILES}
76  * which is a system-level permission that will not be granted to normal apps.
77  */
78 public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
79     private static final String TAG = "ArtManagerService";
80     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
81 
82     // Package name used to create the profile directory layout when
83     // taking a snapshot of the boot image profile.
84     private static final String BOOT_IMAGE_ANDROID_PACKAGE = "android";
85     // Profile name used for the boot image profile.
86     private static final String BOOT_IMAGE_PROFILE_NAME = "android.prof";
87 
88     private final Context mContext;
89     private final IPackageManager mPackageManager;
90     private final Object mInstallLock;
91     @GuardedBy("mInstallLock")
92     private final Installer mInstaller;
93 
94     private final Handler mHandler;
95 
96     static {
verifyTronLoggingConstants()97         verifyTronLoggingConstants();
98     }
99 
ArtManagerService(Context context, IPackageManager pm, Installer installer, Object installLock)100     public ArtManagerService(Context context, IPackageManager pm, Installer installer,
101             Object installLock) {
102         mContext = context;
103         mPackageManager = pm;
104         mInstaller = installer;
105         mInstallLock = installLock;
106         mHandler = new Handler(BackgroundThread.getHandler().getLooper());
107 
108         LocalServices.addService(ArtManagerInternal.class, new ArtManagerInternalImpl());
109     }
110 
checkAndroidPermissions(int callingUid, String callingPackage)111     private boolean checkAndroidPermissions(int callingUid, String callingPackage) {
112         // Callers always need this permission
113         mContext.enforceCallingOrSelfPermission(
114                 android.Manifest.permission.READ_RUNTIME_PROFILES, TAG);
115 
116         // Callers also need the ability to read usage statistics
117         switch (mContext.getSystemService(AppOpsManager.class)
118                 .noteOp(AppOpsManager.OP_GET_USAGE_STATS, callingUid, callingPackage)) {
119             case AppOpsManager.MODE_ALLOWED:
120                 return true;
121             case AppOpsManager.MODE_DEFAULT:
122                 mContext.enforceCallingOrSelfPermission(
123                         android.Manifest.permission.PACKAGE_USAGE_STATS, TAG);
124                 return true;
125             default:
126                 return false;
127         }
128     }
129 
130     /**
131      * Checks if the calling user is the shell user and if it is, it checks if it can
132      * to take a profile snapshot of the give package:
133      *   - on debuggable builds the shell user can take profile snapshots of any app.
134      *   - on non-debuggable builds the shell user can only take snapshots of debuggable apps.
135      *
136      * Returns true iff the callingUid is the shell uid and the shell is allowed snapshot profiles.
137      *
138      * Note that the root users will go through the regular {@link #checkAndroidPermissions) checks.
139      */
checkShellPermissions(@rofileType int profileType, String packageName, int callingUid)140     private boolean checkShellPermissions(@ProfileType int profileType, String packageName,
141             int callingUid) {
142         if (callingUid != Process.SHELL_UID) {
143             return false;
144         }
145         if (RoSystemProperties.DEBUGGABLE) {
146             return true;
147         }
148         if (profileType == ArtManager.PROFILE_BOOT_IMAGE) {
149             // The shell cannot profile the boot image on non-debuggable builds.
150             return false;
151         }
152         PackageInfo info = null;
153         try {
154             info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0);
155         } catch (RemoteException ignored) {
156             // Should not happen.
157         }
158         if (info == null) {
159             return false;
160         }
161 
162         // On user builds the shell can only profile debuggable apps.
163         return (info.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE)
164                 == ApplicationInfo.FLAG_DEBUGGABLE;
165     }
166 
167 
168     @Override
snapshotRuntimeProfile(@rofileType int profileType, @Nullable String packageName, @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback, String callingPackage)169     public void snapshotRuntimeProfile(@ProfileType int profileType, @Nullable String packageName,
170             @Nullable String codePath, @NonNull ISnapshotRuntimeProfileCallback callback,
171             String callingPackage) {
172         int callingUid = Binder.getCallingUid();
173         if (!checkShellPermissions(profileType, packageName, callingUid) &&
174                 !checkAndroidPermissions(callingUid, callingPackage)) {
175             try {
176                 callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
177             } catch (RemoteException ignored) {
178             }
179             return;
180         }
181 
182         // Sanity checks on the arguments.
183         Preconditions.checkNotNull(callback);
184 
185         boolean bootImageProfile = profileType == ArtManager.PROFILE_BOOT_IMAGE;
186         if (!bootImageProfile) {
187             Preconditions.checkStringNotEmpty(codePath);
188             Preconditions.checkStringNotEmpty(packageName);
189         }
190 
191         // Verify that runtime profiling is enabled.
192         if (!isRuntimeProfilingEnabled(profileType, callingPackage)) {
193             throw new IllegalStateException("Runtime profiling is not enabled for " + profileType);
194         }
195 
196         if (DEBUG) {
197             Slog.d(TAG, "Requested snapshot for " + packageName + ":" + codePath);
198         }
199 
200         if (bootImageProfile) {
201             snapshotBootImageProfile(callback);
202         } else {
203             snapshotAppProfile(packageName, codePath, callback);
204         }
205     }
206 
snapshotAppProfile(String packageName, String codePath, ISnapshotRuntimeProfileCallback callback)207     private void snapshotAppProfile(String packageName, String codePath,
208             ISnapshotRuntimeProfileCallback callback) {
209         PackageInfo info = null;
210         try {
211             // Note that we use the default user 0 to retrieve the package info.
212             // This doesn't really matter because for user 0 we always get a package back (even if
213             // it's not installed for the user 0). It is ok because we only care about the code
214             // paths and not if the package is enabled or not for the user.
215 
216             // TODO(calin): consider adding an API to PMS which can retrieve the
217             // PackageParser.Package.
218             info = mPackageManager.getPackageInfo(packageName, /*flags*/ 0, /*userId*/ 0);
219         } catch (RemoteException ignored) {
220             // Should not happen.
221         }
222         if (info == null) {
223             postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_PACKAGE_NOT_FOUND);
224             return;
225         }
226 
227         boolean pathFound = info.applicationInfo.getBaseCodePath().equals(codePath);
228         String splitName = null;
229         String[] splitCodePaths = info.applicationInfo.getSplitCodePaths();
230         if (!pathFound && (splitCodePaths != null)) {
231             for (int i = splitCodePaths.length - 1; i >= 0; i--) {
232                 if (splitCodePaths[i].equals(codePath)) {
233                     pathFound = true;
234                     splitName = info.applicationInfo.splitNames[i];
235                     break;
236                 }
237             }
238         }
239         if (!pathFound) {
240             postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_CODE_PATH_NOT_FOUND);
241             return;
242         }
243 
244         // All good, create the profile snapshot.
245         int appId = UserHandle.getAppId(info.applicationInfo.uid);
246         if (appId < 0) {
247             postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
248             Slog.wtf(TAG, "AppId is -1 for package: " + packageName);
249             return;
250         }
251 
252         createProfileSnapshot(packageName, ArtManager.getProfileName(splitName), codePath,
253                 appId, callback);
254         // Destroy the snapshot, we no longer need it.
255         destroyProfileSnapshot(packageName, ArtManager.getProfileName(splitName));
256     }
257 
createProfileSnapshot(String packageName, String profileName, String classpath, int appId, ISnapshotRuntimeProfileCallback callback)258     private void createProfileSnapshot(String packageName, String profileName, String classpath,
259             int appId, ISnapshotRuntimeProfileCallback callback) {
260         // Ask the installer to snapshot the profile.
261         synchronized (mInstallLock) {
262             try {
263                 if (!mInstaller.createProfileSnapshot(appId, packageName, profileName, classpath)) {
264                     postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
265                     return;
266                 }
267             } catch (InstallerException e) {
268                 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
269                 return;
270             }
271         }
272 
273         // Open the snapshot and invoke the callback.
274         File snapshotProfile = ArtManager.getProfileSnapshotFileForName(packageName, profileName);
275 
276         ParcelFileDescriptor fd = null;
277         try {
278             fd = ParcelFileDescriptor.open(snapshotProfile, ParcelFileDescriptor.MODE_READ_ONLY);
279             if (fd == null || !fd.getFileDescriptor().valid()) {
280                 Slog.wtf(TAG,
281                         "ParcelFileDescriptor.open returned an invalid descriptor for "
282                                 + packageName + ":" + snapshotProfile + ". isNull=" + (fd == null));
283                 postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
284             } else {
285                 postSuccess(packageName, fd, callback);
286             }
287         } catch (FileNotFoundException e) {
288             Slog.w(TAG, "Could not open snapshot profile for " + packageName + ":"
289                     + snapshotProfile, e);
290             postError(callback, packageName, ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
291         }
292     }
293 
destroyProfileSnapshot(String packageName, String profileName)294     private void destroyProfileSnapshot(String packageName, String profileName) {
295         if (DEBUG) {
296             Slog.d(TAG, "Destroying profile snapshot for" + packageName + ":" + profileName);
297         }
298 
299         synchronized (mInstallLock) {
300             try {
301                 mInstaller.destroyProfileSnapshot(packageName, profileName);
302             } catch (InstallerException e) {
303                 Slog.e(TAG, "Failed to destroy profile snapshot for " +
304                     packageName + ":" + profileName, e);
305             }
306         }
307     }
308 
309     @Override
isRuntimeProfilingEnabled(@rofileType int profileType, String callingPackage)310     public boolean isRuntimeProfilingEnabled(@ProfileType int profileType, String callingPackage) {
311         int callingUid = Binder.getCallingUid();
312         if (callingUid != Process.SHELL_UID && !checkAndroidPermissions(callingUid, callingPackage)) {
313             return false;
314         }
315 
316         switch (profileType) {
317             case ArtManager.PROFILE_APPS :
318                 return SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
319             case ArtManager.PROFILE_BOOT_IMAGE:
320                 return (Build.IS_USERDEBUG || Build.IS_ENG) &&
321                         SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false) &&
322                         SystemProperties.getBoolean("dalvik.vm.profilebootimage", false);
323             default:
324                 throw new IllegalArgumentException("Invalid profile type:" + profileType);
325         }
326     }
327 
snapshotBootImageProfile(ISnapshotRuntimeProfileCallback callback)328     private void snapshotBootImageProfile(ISnapshotRuntimeProfileCallback callback) {
329         // Combine the profiles for boot classpath and system server classpath.
330         // This avoids having yet another type of profiles and simplifies the processing.
331         String classpath = String.join(":", Os.getenv("BOOTCLASSPATH"),
332                 Os.getenv("SYSTEMSERVERCLASSPATH"));
333 
334         // Create the snapshot.
335         createProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME, classpath,
336                 /*appId*/ -1, callback);
337         // Destroy the snapshot, we no longer need it.
338         destroyProfileSnapshot(BOOT_IMAGE_ANDROID_PACKAGE, BOOT_IMAGE_PROFILE_NAME);
339     }
340 
341     /**
342      * Post {@link ISnapshotRuntimeProfileCallback#onError(int)} with the given error message
343      * on the internal {@code mHandler}.
344      */
postError(ISnapshotRuntimeProfileCallback callback, String packageName, int errCode)345     private void postError(ISnapshotRuntimeProfileCallback callback, String packageName,
346             int errCode) {
347         if (DEBUG) {
348             Slog.d(TAG, "Failed to snapshot profile for " + packageName + " with error: " +
349                     errCode);
350         }
351         mHandler.post(() -> {
352             try {
353                 callback.onError(errCode);
354             } catch (Exception e) {
355                 Slog.w(TAG, "Failed to callback after profile snapshot for " + packageName, e);
356             }
357         });
358     }
359 
postSuccess(String packageName, ParcelFileDescriptor fd, ISnapshotRuntimeProfileCallback callback)360     private void postSuccess(String packageName, ParcelFileDescriptor fd,
361             ISnapshotRuntimeProfileCallback callback) {
362         if (DEBUG) {
363             Slog.d(TAG, "Successfully snapshot profile for " + packageName);
364         }
365         mHandler.post(() -> {
366             try {
367                 // Double check that the descriptor is still valid.
368                 // We've seen production issues (b/76028139) where this can turn invalid (there are
369                 // suspicions around the finalizer behaviour).
370                 if (fd.getFileDescriptor().valid()) {
371                     callback.onSuccess(fd);
372                 } else {
373                     Slog.wtf(TAG, "The snapshot FD became invalid before posting the result for "
374                             + packageName);
375                     callback.onError(ArtManager.SNAPSHOT_FAILED_INTERNAL_ERROR);
376                 }
377             } catch (Exception e) {
378                 Slog.w(TAG,
379                         "Failed to call onSuccess after profile snapshot for " + packageName, e);
380             } finally {
381                 IoUtils.closeQuietly(fd);
382             }
383         });
384     }
385 
386     /**
387      * Prepare the application profiles.
388      * For all code paths:
389      *   - create the current primary profile to save time at app startup time.
390      *   - copy the profiles from the associated dex metadata file to the reference profile.
391      */
prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user, boolean updateReferenceProfileContent)392     public void prepareAppProfiles(PackageParser.Package pkg, @UserIdInt int user,
393             boolean updateReferenceProfileContent) {
394         final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
395         if (user < 0) {
396             Slog.wtf(TAG, "Invalid user id: " + user);
397             return;
398         }
399         if (appId < 0) {
400             Slog.wtf(TAG, "Invalid app id: " + appId);
401             return;
402         }
403         try {
404             ArrayMap<String, String> codePathsProfileNames = getPackageProfileNames(pkg);
405             for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) {
406                 String codePath = codePathsProfileNames.keyAt(i);
407                 String profileName = codePathsProfileNames.valueAt(i);
408                 String dexMetadataPath = null;
409                 // Passing the dex metadata file to the prepare method will update the reference
410                 // profile content. As such, we look for the dex metadata file only if we need to
411                 // perform an update.
412                 if (updateReferenceProfileContent) {
413                     File dexMetadata = DexMetadataHelper.findDexMetadataForFile(new File(codePath));
414                     dexMetadataPath = dexMetadata == null ? null : dexMetadata.getAbsolutePath();
415                 }
416                 synchronized (mInstaller) {
417                     boolean result = mInstaller.prepareAppProfile(pkg.packageName, user, appId,
418                             profileName, codePath, dexMetadataPath);
419                     if (!result) {
420                         Slog.e(TAG, "Failed to prepare profile for " +
421                                 pkg.packageName + ":" + codePath);
422                     }
423                 }
424             }
425         } catch (InstallerException e) {
426             Slog.e(TAG, "Failed to prepare profile for " + pkg.packageName, e);
427         }
428     }
429 
430     /**
431      * Prepares the app profiles for a set of users. {@see ArtManagerService#prepareAppProfiles}.
432      */
prepareAppProfiles(PackageParser.Package pkg, int[] user, boolean updateReferenceProfileContent)433     public void prepareAppProfiles(PackageParser.Package pkg, int[] user,
434             boolean updateReferenceProfileContent) {
435         for (int i = 0; i < user.length; i++) {
436             prepareAppProfiles(pkg, user[i], updateReferenceProfileContent);
437         }
438     }
439 
440     /**
441      * Clear the profiles for the given package.
442      */
clearAppProfiles(PackageParser.Package pkg)443     public void clearAppProfiles(PackageParser.Package pkg) {
444         try {
445             ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
446             for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
447                 String profileName = packageProfileNames.valueAt(i);
448                 mInstaller.clearAppProfiles(pkg.packageName, profileName);
449             }
450         } catch (InstallerException e) {
451             Slog.w(TAG, String.valueOf(e));
452         }
453     }
454 
455     /**
456      * Dumps the profiles for the given package.
457      */
dumpProfiles(PackageParser.Package pkg)458     public void dumpProfiles(PackageParser.Package pkg) {
459         final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
460         try {
461             ArrayMap<String, String> packageProfileNames = getPackageProfileNames(pkg);
462             for (int i = packageProfileNames.size() - 1; i >= 0; i--) {
463                 String codePath = packageProfileNames.keyAt(i);
464                 String profileName = packageProfileNames.valueAt(i);
465                 synchronized (mInstallLock) {
466                     mInstaller.dumpProfiles(sharedGid, pkg.packageName, profileName, codePath);
467                 }
468             }
469         } catch (InstallerException e) {
470             Slog.w(TAG, "Failed to dump profiles", e);
471         }
472     }
473 
474     /**
475      * Compile layout resources in a given package.
476      */
compileLayouts(PackageParser.Package pkg)477     public boolean compileLayouts(PackageParser.Package pkg) {
478         try {
479             final String packageName = pkg.packageName;
480             final String apkPath = pkg.baseCodePath;
481             final ApplicationInfo appInfo = pkg.applicationInfo;
482             final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex";
483             if (appInfo.isPrivilegedApp() || appInfo.isEmbeddedDexUsed()
484                     || appInfo.isDefaultToDeviceProtectedStorage()) {
485                 // Privileged apps prefer to load trusted code so they don't use compiled views.
486                 // If the app is not privileged but prefers code integrity, also avoid compiling
487                 // views.
488                 // Also disable the view compiler for protected storage apps since there are
489                 // selinux permissions required for writing to user_de.
490                 return false;
491             }
492             Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
493                     ") to " + outDexFile);
494             long callingId = Binder.clearCallingIdentity();
495             try {
496                 synchronized (mInstallLock) {
497                     return mInstaller.compileLayouts(apkPath, packageName, outDexFile,
498                             appInfo.uid);
499                 }
500             } finally {
501                 Binder.restoreCallingIdentity(callingId);
502             }
503         }
504         catch (Throwable e) {
505             Log.e("PackageManager", "Failed to compile layouts", e);
506             return false;
507         }
508     }
509 
510     /**
511      * Build the profiles names for all the package code paths (excluding resource only paths).
512      * Return the map [code path -> profile name].
513      */
getPackageProfileNames(PackageParser.Package pkg)514     private ArrayMap<String, String> getPackageProfileNames(PackageParser.Package pkg) {
515         ArrayMap<String, String> result = new ArrayMap<>();
516         if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
517             result.put(pkg.baseCodePath, ArtManager.getProfileName(null));
518         }
519         if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
520             for (int i = 0; i < pkg.splitCodePaths.length; i++) {
521                 if ((pkg.splitFlags[i] & ApplicationInfo.FLAG_HAS_CODE) != 0) {
522                     result.put(pkg.splitCodePaths[i], ArtManager.getProfileName(pkg.splitNames[i]));
523                 }
524             }
525         }
526         return result;
527     }
528 
529     // Constants used for logging compilation filter to TRON.
530     // DO NOT CHANGE existing values.
531     //
532     // NOTE: '-1' value is reserved for the case where we cannot produce a valid
533     // PackageOptimizationInfo because the ArtManagerInternal is not ready to be used by the
534     // ActivityMetricsLoggers.
535     private static final int TRON_COMPILATION_FILTER_ERROR = 0;
536     private static final int TRON_COMPILATION_FILTER_UNKNOWN = 1;
537     private static final int TRON_COMPILATION_FILTER_ASSUMED_VERIFIED = 2;
538     private static final int TRON_COMPILATION_FILTER_EXTRACT = 3;
539     private static final int TRON_COMPILATION_FILTER_VERIFY = 4;
540     private static final int TRON_COMPILATION_FILTER_QUICKEN = 5;
541     private static final int TRON_COMPILATION_FILTER_SPACE_PROFILE = 6;
542     private static final int TRON_COMPILATION_FILTER_SPACE = 7;
543     private static final int TRON_COMPILATION_FILTER_SPEED_PROFILE = 8;
544     private static final int TRON_COMPILATION_FILTER_SPEED = 9;
545     private static final int TRON_COMPILATION_FILTER_EVERYTHING_PROFILE = 10;
546     private static final int TRON_COMPILATION_FILTER_EVERYTHING = 11;
547     private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK = 12;
548     private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK = 13;
549     private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK = 14;
550 
551     // Constants used for logging compilation reason to TRON.
552     // DO NOT CHANGE existing values.
553     //
554     // NOTE: '-1' value is reserved for the case where we cannot produce a valid
555     // PackageOptimizationInfo because the ArtManagerInternal is not ready to be used by the
556     // ActivityMetricsLoggers.
557     private static final int TRON_COMPILATION_REASON_ERROR = 0;
558     private static final int TRON_COMPILATION_REASON_UNKNOWN = 1;
559     private static final int TRON_COMPILATION_REASON_FIRST_BOOT = 2;
560     private static final int TRON_COMPILATION_REASON_BOOT = 3;
561     private static final int TRON_COMPILATION_REASON_INSTALL = 4;
562     private static final int TRON_COMPILATION_REASON_BG_DEXOPT = 5;
563     private static final int TRON_COMPILATION_REASON_AB_OTA = 6;
564     private static final int TRON_COMPILATION_REASON_INACTIVE = 7;
565     private static final int TRON_COMPILATION_REASON_SHARED = 8;
566     private static final int TRON_COMPILATION_REASON_INSTALL_WITH_DEX_METADATA = 9;
567 
568     // The annotation to add as a suffix to the compilation reason when dexopt was
569     // performed with dex metadata.
570     public static final String DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION = "-dm";
571 
572     /**
573      * Convert the compilation reason to an int suitable to be logged to TRON.
574      */
getCompilationReasonTronValue(String compilationReason)575     private static int getCompilationReasonTronValue(String compilationReason) {
576         switch (compilationReason) {
577             case "unknown" : return TRON_COMPILATION_REASON_UNKNOWN;
578             case "error" : return TRON_COMPILATION_REASON_ERROR;
579             case "first-boot" : return TRON_COMPILATION_REASON_FIRST_BOOT;
580             case "boot" : return TRON_COMPILATION_REASON_BOOT;
581             case "install" : return TRON_COMPILATION_REASON_INSTALL;
582             case "bg-dexopt" : return TRON_COMPILATION_REASON_BG_DEXOPT;
583             case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA;
584             case "inactive" : return TRON_COMPILATION_REASON_INACTIVE;
585             case "shared" : return TRON_COMPILATION_REASON_SHARED;
586             // This is a special marker for dex metadata installation that does not
587             // have an equivalent as a system property.
588             case "install" + DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION :
589                 return TRON_COMPILATION_REASON_INSTALL_WITH_DEX_METADATA;
590             default: return TRON_COMPILATION_REASON_UNKNOWN;
591         }
592     }
593 
594     /**
595      * Convert the compilation filter to an int suitable to be logged to TRON.
596      */
getCompilationFilterTronValue(String compilationFilter)597     private static int getCompilationFilterTronValue(String compilationFilter) {
598         switch (compilationFilter) {
599             case "error" : return TRON_COMPILATION_FILTER_ERROR;
600             case "unknown" : return TRON_COMPILATION_FILTER_UNKNOWN;
601             case "assume-verified" : return TRON_COMPILATION_FILTER_ASSUMED_VERIFIED;
602             case "extract" : return TRON_COMPILATION_FILTER_EXTRACT;
603             case "verify" : return TRON_COMPILATION_FILTER_VERIFY;
604             case "quicken" : return TRON_COMPILATION_FILTER_QUICKEN;
605             case "space-profile" : return TRON_COMPILATION_FILTER_SPACE_PROFILE;
606             case "space" : return TRON_COMPILATION_FILTER_SPACE;
607             case "speed-profile" : return TRON_COMPILATION_FILTER_SPEED_PROFILE;
608             case "speed" : return TRON_COMPILATION_FILTER_SPEED;
609             case "everything-profile" : return TRON_COMPILATION_FILTER_EVERYTHING_PROFILE;
610             case "everything" : return TRON_COMPILATION_FILTER_EVERYTHING;
611             case "run-from-apk" : return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK;
612             case "run-from-apk-fallback" :
613                 return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
614             case "run-from-vdex-fallback" :
615                 return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
616             default: return TRON_COMPILATION_FILTER_UNKNOWN;
617         }
618     }
619 
verifyTronLoggingConstants()620     private static void verifyTronLoggingConstants() {
621         for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
622             String reason = PackageManagerServiceCompilerMapping.REASON_STRINGS[i];
623             int value = getCompilationReasonTronValue(reason);
624             if (value == TRON_COMPILATION_REASON_ERROR
625                     || value == TRON_COMPILATION_REASON_UNKNOWN) {
626                 throw new IllegalArgumentException("Compilation reason not configured for TRON "
627                         + "logging: " + reason);
628             }
629         }
630     }
631 
632     private class ArtManagerInternalImpl extends ArtManagerInternal {
633         @Override
getPackageOptimizationInfo( ApplicationInfo info, String abi)634         public PackageOptimizationInfo getPackageOptimizationInfo(
635                 ApplicationInfo info, String abi) {
636             String compilationReason;
637             String compilationFilter;
638             try {
639                 String isa = VMRuntime.getInstructionSet(abi);
640                 DexFile.OptimizationInfo optInfo =
641                         DexFile.getDexFileOptimizationInfo(info.getBaseCodePath(), isa);
642                 compilationFilter = optInfo.getStatus();
643                 compilationReason = optInfo.getReason();
644             } catch (FileNotFoundException e) {
645                 Slog.e(TAG, "Could not get optimizations status for " + info.getBaseCodePath(), e);
646                 compilationFilter = "error";
647                 compilationReason = "error";
648             } catch (IllegalArgumentException e) {
649                 Slog.wtf(TAG, "Requested optimization status for " + info.getBaseCodePath()
650                         + " due to an invalid abi " + abi, e);
651                 compilationFilter = "error";
652                 compilationReason = "error";
653             }
654 
655             int compilationFilterTronValue = getCompilationFilterTronValue(compilationFilter);
656             int compilationReasonTronValue = getCompilationReasonTronValue(compilationReason);
657 
658             return new PackageOptimizationInfo(
659                     compilationFilterTronValue, compilationReasonTronValue);
660         }
661     }
662 }
663