Merge "Report non-primary dex files loads to PM"
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 94d24e4..db1162a 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -51,6 +51,7 @@
 import android.view.Display;
 import android.view.DisplayAdjustments;
 
+import dalvik.system.BaseDexClassLoader;
 import dalvik.system.VMRuntime;
 
 import java.io.File;
@@ -600,6 +601,40 @@
 
         VMRuntime.registerAppInfo(profileFile.getPath(), mApplicationInfo.dataDir,
                 codePaths.toArray(new String[codePaths.size()]), foreignDexProfilesFile.getPath());
+
+        // Setup the reporter to notify package manager of any relevant dex loads.
+        // At this point the primary apk is loaded and will not be reported.
+        // Anything loaded from now on will be tracked as a potential secondary
+        // or foreign dex file. The goal is to enable:
+        //    1) monitoring and compilation of secondary dex file
+        //    2) track foreign dex file usage (used to determined the
+        //       compilation filter of apks).
+        if (BaseDexClassLoader.getReporter() != DexLoadReporter.INSTANCE) {
+            // Set the dex load reporter if not already set.
+            // Note that during the app's life cycle different LoadedApks may be
+            // created and loaded (e.g. if two different apps share the same runtime).
+            BaseDexClassLoader.setReporter(DexLoadReporter.INSTANCE);
+        }
+    }
+
+    private static class DexLoadReporter implements BaseDexClassLoader.Reporter {
+        private static final DexLoadReporter INSTANCE = new DexLoadReporter();
+
+        private DexLoadReporter() {}
+
+        @Override
+        public void report(List<String> dexPaths) {
+            if (dexPaths.isEmpty()) {
+                return;
+            }
+            String packageName = ActivityThread.currentPackageName();
+            try {
+                ActivityThread.getPackageManager().notifyDexLoad(
+                        packageName, dexPaths, VMRuntime.getRuntime().vmInstructionSet());
+            } catch (RemoteException re) {
+                Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d753a6e..b9b61db 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -458,6 +458,15 @@
     void notifyPackageUse(String packageName, int reason);
 
     /**
+     * Notify the package manager that a list of dex files have been loaded.
+     *
+     * @param loadingPackageName the name of the package who performs the load
+     * @param dexPats the list of the dex files paths that have been loaded
+     * @param loaderIsa the ISA of the loader process
+     */
+    void notifyDexLoad(String loadingPackageName, in List<String> dexPaths, String loaderIsa);
+
+    /**
      * Ask the package manager to perform dex-opt (if needed) on the given
      * package if it already hasn't done so.
      *
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 34c1470..df1de11 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7639,6 +7639,11 @@
         }
     }
 
+    @Override
+    public void notifyDexLoad(String loadingPackageName, List<String> dexPaths, String loaderIsa) {
+      // TODO(calin): b/32871170
+    }
+
     // TODO: this is not used nor needed. Delete it.
     @Override
     public boolean performDexOptIfNeeded(String packageName) {