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