• 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.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         // Validity 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.getBaseApkPath();
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             final 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.getBaseApkPath(), 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     // In the below constants, the abbreviation DM stands for "DEX metadata".
583     //
584     // NOTE: '-1' value is reserved for the case where we cannot produce a valid
585     // PackageOptimizationInfo because the ArtManagerInternal is not ready to be used by the
586     // ActivityMetricsLoggers.
587     private static final int TRON_COMPILATION_REASON_ERROR = 0;
588     private static final int TRON_COMPILATION_REASON_UNKNOWN = 1;
589     private static final int TRON_COMPILATION_REASON_FIRST_BOOT = 2;
590     private static final int TRON_COMPILATION_REASON_BOOT_DEPRECATED_SINCE_S = 3;
591     private static final int TRON_COMPILATION_REASON_INSTALL = 4;
592     private static final int TRON_COMPILATION_REASON_BG_DEXOPT = 5;
593     private static final int TRON_COMPILATION_REASON_AB_OTA = 6;
594     private static final int TRON_COMPILATION_REASON_INACTIVE = 7;
595     private static final int TRON_COMPILATION_REASON_SHARED = 8;
596     private static final int TRON_COMPILATION_REASON_INSTALL_WITH_DM = 9;
597     private static final int TRON_COMPILATION_REASON_INSTALL_FAST = 10;
598     private static final int TRON_COMPILATION_REASON_INSTALL_BULK = 11;
599     private static final int TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY = 12;
600     private static final int TRON_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED = 13;
601     private static final int TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED = 14;
602     private static final int TRON_COMPILATION_REASON_INSTALL_FAST_WITH_DM = 15;
603     private static final int TRON_COMPILATION_REASON_INSTALL_BULK_WITH_DM = 16;
604     private static final int TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY_WITH_DM = 17;
605     private static final int TRON_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED_WITH_DM = 18;
606     private static final int
607             TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED_WITH_DM = 19;
608     private static final int TRON_COMPILATION_REASON_BOOT_AFTER_OTA = 20;
609     private static final int TRON_COMPILATION_REASON_POST_BOOT = 21;
610     private static final int TRON_COMPILATION_REASON_CMDLINE = 22;
611 
612     // The annotation to add as a suffix to the compilation reason when dexopt was
613     // performed with dex metadata.
614     public static final String DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION = "-dm";
615 
616     /**
617      * Convert the compilation reason to an int suitable to be logged to TRON.
618      */
getCompilationReasonTronValue(String compilationReason)619     private static int getCompilationReasonTronValue(String compilationReason) {
620         switch (compilationReason) {
621             case "cmdline" : return TRON_COMPILATION_REASON_CMDLINE;
622             case "error" : return TRON_COMPILATION_REASON_ERROR;
623             case "first-boot" : return TRON_COMPILATION_REASON_FIRST_BOOT;
624             case "boot-after-ota": return TRON_COMPILATION_REASON_BOOT_AFTER_OTA;
625             case "post-boot" : return TRON_COMPILATION_REASON_POST_BOOT;
626             case "install" : return TRON_COMPILATION_REASON_INSTALL;
627             case "bg-dexopt" : return TRON_COMPILATION_REASON_BG_DEXOPT;
628             case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA;
629             case "inactive" : return TRON_COMPILATION_REASON_INACTIVE;
630             case "shared" : return TRON_COMPILATION_REASON_SHARED;
631             case "install-fast" :
632                 return TRON_COMPILATION_REASON_INSTALL_FAST;
633             case "install-bulk" :
634                 return TRON_COMPILATION_REASON_INSTALL_BULK;
635             case "install-bulk-secondary" :
636                 return TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY;
637             case "install-bulk-downgraded" :
638                 return TRON_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED;
639             case "install-bulk-secondary-downgraded" :
640                 return TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED;
641             // These are special markers for dex metadata installation that do not
642             // have an equivalent as a system property.
643             case "install" + DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION :
644                 return TRON_COMPILATION_REASON_INSTALL_WITH_DM;
645             case "install-fast" + DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION :
646                 return TRON_COMPILATION_REASON_INSTALL_FAST_WITH_DM;
647             case "install-bulk" + DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION :
648                 return TRON_COMPILATION_REASON_INSTALL_BULK_WITH_DM;
649             case "install-bulk-secondary" + DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION :
650                 return TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY_WITH_DM;
651             case "install-bulk-downgraded" + DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION :
652                 return TRON_COMPILATION_REASON_INSTALL_BULK_DOWNGRADED_WITH_DM;
653             case "install-bulk-secondary-downgraded" + DEXOPT_REASON_WITH_DEX_METADATA_ANNOTATION :
654                 return TRON_COMPILATION_REASON_INSTALL_BULK_SECONDARY_DOWNGRADED_WITH_DM;
655             default: return TRON_COMPILATION_REASON_UNKNOWN;
656         }
657     }
658 
659     /**
660      * Convert the compilation filter to an int suitable to be logged to TRON.
661      */
getCompilationFilterTronValue(String compilationFilter)662     private static int getCompilationFilterTronValue(String compilationFilter) {
663         switch (compilationFilter) {
664             case "error" : return TRON_COMPILATION_FILTER_ERROR;
665             case "unknown" : return TRON_COMPILATION_FILTER_UNKNOWN;
666             case "assume-verified" : return TRON_COMPILATION_FILTER_ASSUMED_VERIFIED;
667             case "extract" : return TRON_COMPILATION_FILTER_EXTRACT;
668             case "verify" : return TRON_COMPILATION_FILTER_VERIFY;
669             case "quicken" : return TRON_COMPILATION_FILTER_QUICKEN;
670             case "space-profile" : return TRON_COMPILATION_FILTER_SPACE_PROFILE;
671             case "space" : return TRON_COMPILATION_FILTER_SPACE;
672             case "speed-profile" : return TRON_COMPILATION_FILTER_SPEED_PROFILE;
673             case "speed" : return TRON_COMPILATION_FILTER_SPEED;
674             case "everything-profile" : return TRON_COMPILATION_FILTER_EVERYTHING_PROFILE;
675             case "everything" : return TRON_COMPILATION_FILTER_EVERYTHING;
676             case "run-from-apk" : return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK;
677             case "run-from-apk-fallback" :
678                 return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
679             case "run-from-vdex-fallback" :
680                 return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
681             case "assume-verified-iorap" : return TRON_COMPILATION_FILTER_ASSUMED_VERIFIED_IORAP;
682             case "extract-iorap" : return TRON_COMPILATION_FILTER_EXTRACT_IORAP;
683             case "verify-iorap" : return TRON_COMPILATION_FILTER_VERIFY_IORAP;
684             case "quicken-iorap" : return TRON_COMPILATION_FILTER_QUICKEN_IORAP;
685             case "space-profile-iorap" : return TRON_COMPILATION_FILTER_SPACE_PROFILE_IORAP;
686             case "space-iorap" : return TRON_COMPILATION_FILTER_SPACE_IORAP;
687             case "speed-profile-iorap" : return TRON_COMPILATION_FILTER_SPEED_PROFILE_IORAP;
688             case "speed-iorap" : return TRON_COMPILATION_FILTER_SPEED_IORAP;
689             case "everything-profile-iorap" :
690                 return TRON_COMPILATION_FILTER_EVERYTHING_PROFILE_IORAP;
691             case "everything-iorap" : return TRON_COMPILATION_FILTER_EVERYTHING_IORAP;
692             case "run-from-apk-iorap" : return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_IORAP;
693             case "run-from-apk-fallback-iorap" :
694                 return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK_IORAP;
695             case "run-from-vdex-fallback-iorap" :
696                 return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK_IORAP;
697             default: return TRON_COMPILATION_FILTER_UNKNOWN;
698         }
699     }
700 
verifyTronLoggingConstants()701     private static void verifyTronLoggingConstants() {
702         for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
703             String reason = PackageManagerServiceCompilerMapping.REASON_STRINGS[i];
704             int value = getCompilationReasonTronValue(reason);
705             if (value == TRON_COMPILATION_REASON_ERROR
706                     || value == TRON_COMPILATION_REASON_UNKNOWN) {
707                 throw new IllegalArgumentException("Compilation reason not configured for TRON "
708                         + "logging: " + reason);
709             }
710         }
711     }
712 
713     private class ArtManagerInternalImpl extends ArtManagerInternal {
714         private static final String IORAP_DIR = "/data/misc/iorapd";
715         private static final String TAG = "ArtManagerInternalImpl";
716 
717         @Override
getPackageOptimizationInfo( ApplicationInfo info, String abi, String activityName)718         public PackageOptimizationInfo getPackageOptimizationInfo(
719                 ApplicationInfo info, String abi, String activityName) {
720             String compilationReason;
721             String compilationFilter;
722             try {
723                 String isa = VMRuntime.getInstructionSet(abi);
724                 DexFile.OptimizationInfo optInfo =
725                         DexFile.getDexFileOptimizationInfo(info.getBaseCodePath(), isa);
726                 compilationFilter = optInfo.getStatus();
727                 compilationReason = optInfo.getReason();
728             } catch (FileNotFoundException e) {
729                 Slog.e(TAG, "Could not get optimizations status for " + info.getBaseCodePath(), e);
730                 compilationFilter = "error";
731                 compilationReason = "error";
732             } catch (IllegalArgumentException e) {
733                 Slog.wtf(TAG, "Requested optimization status for " + info.getBaseCodePath()
734                         + " due to an invalid abi " + abi, e);
735                 compilationFilter = "error";
736                 compilationReason = "error";
737             }
738 
739             if (checkIorapCompiledTrace(info.packageName, activityName, info.longVersionCode)) {
740                 compilationFilter = compilationFilter + "-iorap";
741             }
742 
743             int compilationFilterTronValue = getCompilationFilterTronValue(compilationFilter);
744             int compilationReasonTronValue = getCompilationReasonTronValue(compilationReason);
745 
746             return new PackageOptimizationInfo(
747                     compilationFilterTronValue, compilationReasonTronValue);
748         }
749 
750         /*
751          * Checks the existence of IORap compiled trace for an app.
752          *
753          * @return true if the compiled trace exists and the size is greater than 1kb.
754          */
checkIorapCompiledTrace( String packageName, String activityName, long version)755         private boolean checkIorapCompiledTrace(
756                 String packageName, String activityName, long version) {
757             // For example: /data/misc/iorapd/com.google.android.GoogleCamera/
758             // 60092239/com.android.camera.CameraLauncher/compiled_traces/compiled_trace.pb
759             Path tracePath = Paths.get(IORAP_DIR,
760                                        packageName,
761                                        Long.toString(version),
762                                        activityName,
763                                        "compiled_traces",
764                                        "compiled_trace.pb");
765             try {
766                 boolean exists =  Files.exists(tracePath);
767                 if (DEBUG) {
768                     Log.d(TAG, tracePath.toString() + (exists? " exists" : " doesn't exist"));
769                 }
770                 if (exists) {
771                     long bytes = Files.size(tracePath);
772                     if (DEBUG) {
773                         Log.d(TAG, tracePath.toString() + " size is " + Long.toString(bytes));
774                     }
775                     return bytes > 0L;
776                 }
777                 return exists;
778             } catch (IOException e) {
779                 Log.d(TAG, e.getMessage());
780                 return false;
781             }
782         }
783     }
784 }
785