Change app selection policy for post-OTA verification

Changes the policy for selecting packages which will be pre-verified
during post-OTA boot animation.

For Nx to Ny, an app is pre-verified if used in the foreground in the
last 7 days, or if its APK was loaded by other apps.

For M to N (or early N builds without detailed stats), an app is
pre-verified if it has any recorded use in the last 7 days.

Bug: 27902702
Bug: 27350503
Change-Id: I2b38daf017ecd0e5aa5ed596ed9351cffa03dbcb
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index b3ac05c..f620274 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -137,21 +137,14 @@
         boolean isProfileGuidedFilter = DexFile.isProfileGuidedCompilerFilter(targetCompilerFilter);
         // If any part of the app is used by other apps, we cannot use profile-guided
         // compilation.
-        // Skip the check for forward locked packages since they don't share their code.
-        if (isProfileGuidedFilter && !pkg.isForwardLocked()) {
-            for (String path : paths) {
-                if (isUsedByOtherApps(path)) {
-                    checkProfiles = false;
+        if (isProfileGuidedFilter && isUsedByOtherApps(pkg)) {
+            checkProfiles = false;
 
-                    targetCompilerFilter = getNonProfileGuidedCompilerFilter(targetCompilerFilter);
-                    if (DexFile.isProfileGuidedCompilerFilter(targetCompilerFilter)) {
-                        throw new IllegalStateException(targetCompilerFilter);
-                    }
-                    isProfileGuidedFilter = false;
-
-                    break;
-                }
+            targetCompilerFilter = getNonProfileGuidedCompilerFilter(targetCompilerFilter);
+            if (DexFile.isProfileGuidedCompilerFilter(targetCompilerFilter)) {
+                throw new IllegalStateException(targetCompilerFilter);
             }
+            isProfileGuidedFilter = false;
         }
 
         // If we're asked to take profile updates into account, check now.
@@ -281,20 +274,34 @@
         mSystemReady = true;
     }
 
-    private boolean isUsedByOtherApps(String apkPath) {
-        try {
-            apkPath = new File(apkPath).getCanonicalPath();
-        } catch (IOException e) {
-            // Log an error but continue without it.
-            Slog.w(TAG, "Failed to get canonical path", e);
+    /**
+     * Returns true if the profiling data collected for the given app indicate
+     * that the apps's APK has been loaded by another app.
+     * Note that this returns false for all forward-locked apps and apps without
+     * any collected profiling data.
+     */
+    public static boolean isUsedByOtherApps(PackageParser.Package pkg) {
+        if (pkg.isForwardLocked()) {
+            // Skip the check for forward locked packages since they don't share their code.
+            return false;
         }
-        String useMarker = apkPath.replace('/', '@');
-        final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
-        for (int i = 0; i < currentUserIds.length; i++) {
-            File profileDir = Environment.getDataProfilesDeForeignDexDirectory(currentUserIds[i]);
-            File foreignUseMark = new File(profileDir, useMarker);
-            if (foreignUseMark.exists()) {
-                return true;
+
+        for (String apkPath : pkg.getAllCodePathsExcludingResourceOnly()) {
+            try {
+                apkPath = new File(apkPath).getCanonicalPath();
+            } catch (IOException e) {
+                // Log an error but continue without it.
+                Slog.w(TAG, "Failed to get canonical path", e);
+            }
+            String useMarker = apkPath.replace('/', '@');
+            final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
+            for (int i = 0; i < currentUserIds.length; i++) {
+                File profileDir =
+                        Environment.getDataProfilesDeForeignDexDirectory(currentUserIds[i]);
+                File foreignUseMark = new File(profileDir, useMarker);
+                if (foreignUseMark.exists()) {
+                    return true;
+                }
             }
         }
         return false;