[RFC] Special case system apps for profile optimizations

If the OTA updates a system app which was previously preopted to a non-
preopted state the app might end up being verified at runtime. That's
because by default the apps are verify-profile but for preopted apps
there's no profile.

Do a hacky check to ensure that if we have no profiles (a reasonable
indication that before the OTA the app was preopted) system apps get
compiled with a non-profile filter (by default interpret-only).

Bug: 30032273
Test: Andreas "has verified that Calin's change to A/B works as expected
and promotes things like SystemUI to speed. From my side, that's
ready to be merged"

Change-Id: I7a052a8ea76cab7f649dc993237ea05534d6c4b9
(cherry picked from commit 0bd776207999ccba17e5adb163710bd7b16ac907)
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index d8be2b6..93fc13c 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -286,6 +286,11 @@
     }
 
     /** {@hide} */
+    public static File getReferenceProfile(String packageName) {
+        return buildPath(getDataDirectory(), "misc", "profiles", "ref", packageName);
+    }
+
+    /** {@hide} */
     public static File getDataProfilesDePackageDirectory(int userId, String packageName) {
         return buildPath(getDataProfilesDeDirectory(userId), packageName);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7124135..4c2ec34 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -95,6 +95,7 @@
 import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
 import static com.android.server.pm.PackageManagerServiceCompilerMapping.getFullCompilerFilter;
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getNonProfileGuidedCompilerFilter;
 import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
 import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
 import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
@@ -2793,7 +2794,7 @@
                     }
                 }
 
-                int[] stats = performDexOpt(coreApps, false,
+                int[] stats = performDexOptUpgrade(coreApps, false,
                         getCompilerFilterForReason(REASON_CORE_APP));
 
                 final int elapsedTimeSeconds =
@@ -7300,7 +7301,7 @@
         }
 
         final long startTime = System.nanoTime();
-        final int[] stats = performDexOpt(pkgs, mIsPreNUpgrade /* showDialog */,
+        final int[] stats = performDexOptUpgrade(pkgs, mIsPreNUpgrade /* showDialog */,
                     getCompilerFilterForReason(causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT));
 
         final int elapsedTimeSeconds =
@@ -7319,7 +7320,7 @@
      * which are (in order) {@code numberOfPackagesOptimized}, {@code numberOfPackagesSkipped}
      * and {@code numberOfPackagesFailed}.
      */
-    private int[] performDexOpt(List<PackageParser.Package> pkgs, boolean showDialog,
+    private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog,
             String compilerFilter) {
 
         int numberOfPackagesVisited = 0;
@@ -7353,6 +7354,19 @@
                 }
             }
 
+            // If the OTA updates a system app which was previously preopted to a non-preopted state
+            // the app might end up being verified at runtime. That's because by default the apps
+            // are verify-profile but for preopted apps there's no profile.
+            // Do a hacky check to ensure that if we have no profiles (a reasonable indication
+            // that before the OTA the app was preopted) the app gets compiled with a non-profile
+            // filter (by default interpret-only).
+            // Note that at this stage unused apps are already filtered.
+            if (isSystemApp(pkg) &&
+                    DexFile.isProfileGuidedCompilerFilter(compilerFilter) &&
+                    !Environment.getReferenceProfile(pkg.packageName).exists()) {
+                compilerFilter = getNonProfileGuidedCompilerFilter(compilerFilter);
+            }
+
             // checkProfiles is false to avoid merging profiles during boot which
             // might interfere with background compilation (b/28612421).
             // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will