Support secondary dex compilation.

Bug: 249984283
Test: atest ArtServiceTests
Test: m test-art-host-gtest-art_artd_tests
Test: m test-art-host-gtest-art_runtime_tests
Test: adb shell pm art optimize-package --secondary-dex -m speed-profile -f com.google.android.gms
Ignore-AOSP-First: ART Services.
Change-Id: I7ebe2aa745d0da31242034a27f92b24dbdb08740
diff --git a/libartservice/service/java/com/android/server/art/DexOptimizer.java b/libartservice/service/java/com/android/server/art/DexOptimizer.java
index 078f698..8cfe8ad 100644
--- a/libartservice/service/java/com/android/server/art/DexOptimizer.java
+++ b/libartservice/service/java/com/android/server/art/DexOptimizer.java
@@ -86,11 +86,6 @@
     public final List<DexContainerFileOptimizeResult> dexopt() throws RemoteException {
         List<DexContainerFileOptimizeResult> results = new ArrayList<>();
 
-        String targetCompilerFilter = adjustCompilerFilter(mParams.getCompilerFilter());
-        if (targetCompilerFilter.equals(OptimizeParams.COMPILER_FILTER_NOOP)) {
-            return results;
-        }
-
         for (DexInfoType dexInfo : getDexInfoList()) {
             ProfilePath profile = null;
             boolean succeeded = true;
@@ -99,7 +94,10 @@
                     continue;
                 }
 
-                String compilerFilter = targetCompilerFilter;
+                String compilerFilter = adjustCompilerFilter(mParams.getCompilerFilter(), dexInfo);
+                if (compilerFilter.equals(OptimizeParams.COMPILER_FILTER_NOOP)) {
+                    continue;
+                }
 
                 boolean needsToBeShared = needsToBeShared(dexInfo);
                 boolean isOtherReadable = true;
@@ -138,7 +136,8 @@
                         DexFile.isProfileGuidedCompilerFilter(compilerFilter);
                 Utils.check(isProfileGuidedCompilerFilter == (profile != null));
 
-                boolean canBePublic = !isProfileGuidedCompilerFilter || isOtherReadable;
+                boolean canBePublic = (!isProfileGuidedCompilerFilter || isOtherReadable)
+                        && isDexFilePublic(dexInfo);
                 Utils.check(Utils.implies(needsToBeShared, canBePublic));
                 PermissionSettings permissionSettings = getPermissionSettings(dexInfo, canBePublic);
 
@@ -245,7 +244,8 @@
     }
 
     @NonNull
-    private String adjustCompilerFilter(@NonNull String targetCompilerFilter) {
+    private String adjustCompilerFilter(
+            @NonNull String targetCompilerFilter, @NonNull DexInfoType dexInfo) {
         if (mInjector.isSystemUiPackage(mPkgState.getPackageName())) {
             String systemUiCompilerFilter = getSystemUiCompilerFilter();
             if (!systemUiCompilerFilter.isEmpty()) {
@@ -264,6 +264,12 @@
             return DexFile.getSafeModeCompilerFilter(targetCompilerFilter);
         }
 
+        // We cannot do AOT compilation if we don't have a valid class loader context.
+        if (dexInfo.classLoaderContext() == null
+                && DexFile.isOptimizedCompilerFilter(targetCompilerFilter)) {
+            return "verify";
+        }
+
         return targetCompilerFilter;
     }
 
@@ -346,6 +352,9 @@
 
         // The result should come from artd even if all the bits of `dexoptTrigger` are set
         // because the result also contains information about the usable VDEX file.
+        // Note that the class loader context can be null. In that case, we intentionally pass the
+        // null value down to lower levels to indicate that the class loader context check should be
+        // skipped because we are only going to verify the dex code (see `adjustCompilerFilter`).
         GetDexoptNeededResult result = mInjector.getArtd().getDexoptNeeded(
                 target.dexInfo().dexPath(), target.isa(), target.dexInfo().classLoaderContext(),
                 target.compilerFilter(), dexoptTrigger);
@@ -470,12 +479,21 @@
     @NonNull protected abstract List<DexInfoType> getDexInfoList();
 
     /** Returns true if the given dex file should be optimized. */
-    protected abstract boolean isOptimizable(@NonNull DexInfoType dexInfo) throws RemoteException;
+    protected abstract boolean isOptimizable(@NonNull DexInfoType dexInfo);
 
-    /** Returns true if the artifacts should be shared with other apps. */
+    /**
+     * Returns true if the artifacts should be shared with other apps. Note that this must imply
+     * {@link #isDexFilePublic(DexInfoType)}.
+     */
     protected abstract boolean needsToBeShared(@NonNull DexInfoType dexInfo);
 
     /**
+     * Returns true if the filesystem permission of the dex file has the "read" bit for "others"
+     * (S_IROTH).
+     */
+    protected abstract boolean isDexFilePublic(@NonNull DexInfoType dexInfo);
+
+    /**
      * Returns a reference profile initialized from an external profile (e.g., a DM profile) if
      * one exists, or null otherwise.
      */
@@ -552,30 +570,30 @@
      *
      * @hide
      */
-    @VisibleForTesting
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
     public static class Injector {
         @NonNull private final Context mContext;
 
-        Injector(@NonNull Context context) {
+        public Injector(@NonNull Context context) {
             mContext = context;
         }
 
-        boolean isSystemUiPackage(@NonNull String packageName) {
+        public boolean isSystemUiPackage(@NonNull String packageName) {
             return packageName.equals(mContext.getString(R.string.config_systemUi));
         }
 
         @NonNull
-        UserManager getUserManager() {
+        public UserManager getUserManager() {
             return Objects.requireNonNull(mContext.getSystemService(UserManager.class));
         }
 
         @NonNull
-        DexUseManager getDexUseManager() {
+        public DexUseManager getDexUseManager() {
             return DexUseManager.getInstance();
         }
 
         @NonNull
-        IArtd getArtd() {
+        public IArtd getArtd() {
             return Utils.getArtd();
         }
     }