Merge "Notify DexManager about new package installs"
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e03ac00..23a6a90 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1866,6 +1866,18 @@
                     res.removedInfo.args.doPostDeleteLI(true);
                 }
             }
+
+            if (!isEphemeral(res.pkg)) {
+                // Notify DexManager that the package was installed for new users.
+                // The updated users should already be indexed and the package code paths
+                // should not change.
+                // Don't notify the manager for ephemeral apps as they are not expected to
+                // survive long enough to benefit of background optimizations.
+                for (int userId : firstUsers) {
+                    PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
+                    mDexManager.notifyPackageInstalled(info, userId);
+                }
+            }
         }
 
         // If someone is watching installs - notify them
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 6d06838..e809213 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -132,8 +132,8 @@
                 // - new installed splits
                 // If we can't find the owner of the dex we simply do not track it. The impact is
                 // that the dex file will not be considered for offline optimizations.
-                // TODO(calin): add hooks for install/uninstall notifications to
-                // capture new or obsolete packages.
+                // TODO(calin): add hooks for move/uninstall notifications to
+                // capture package moves or obsolete packages.
                 if (DEBUG) {
                     Slog.i(TAG, "Could not find owning package for dex file: " + dexPath);
                 }
@@ -157,6 +157,20 @@
         }
     }
 
+    public void notifyPackageInstalled(PackageInfo info, int userId) {
+        cachePackageCodeLocation(info, userId);
+    }
+
+    private void cachePackageCodeLocation(PackageInfo info, int userId) {
+        PackageCodeLocations pcl = mPackageCodeLocationsCache.get(info.packageName);
+        if (pcl != null) {
+            pcl.mergeAppDataDirs(info.applicationInfo, userId);
+        } else {
+            mPackageCodeLocationsCache.put(info.packageName,
+                new PackageCodeLocations(info.applicationInfo, userId));
+        }
+    }
+
     private void loadInternal(Map<Integer, List<PackageInfo>> existingPackages) {
         Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
         // Cache the code locations for the installed packages. This allows for
@@ -166,13 +180,8 @@
             int userId = entry.getKey();
             for (PackageInfo pi : packageInfoList) {
                 // Cache the code locations.
-                PackageCodeLocations pcl = mPackageCodeLocationsCache.get(pi.packageName);
-                if (pcl != null) {
-                    pcl.mergeAppDataDirs(pi.applicationInfo, userId);
-                } else {
-                    mPackageCodeLocationsCache.put(pi.packageName,
-                        new PackageCodeLocations(pi.applicationInfo, userId));
-                }
+                cachePackageCodeLocation(pi, userId);
+
                 // Cache a map from package name to the set of user ids who installed the package.
                 // We will use it to sync the data and remove obsolete entries from
                 // mPackageDexUsage.
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index b655f3a..2d07e65 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -204,6 +204,46 @@
         assertNull(getPackageUseInfo(mBarUser1));
     }
 
+    @Test
+    public void testNotifyPackageInstallUsedByOther() {
+        TestData newPackage = new TestData("newPackage",
+                VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]), mUser0);
+
+        List<String> newSecondaries = newPackage.getSecondaryDexPaths();
+        // Before we notify about the installation of the newPackage if mFoo
+        // is trying to load something from it we should not find it.
+        notifyDexLoad(mFooUser0, newSecondaries, mUser0);
+        assertNull(getPackageUseInfo(newPackage));
+
+        // Notify about newPackage install and let mFoo load its dexes.
+        mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
+        notifyDexLoad(mFooUser0, newSecondaries, mUser0);
+
+        // We should get back the right info.
+        PackageUseInfo pui = getPackageUseInfo(newPackage);
+        assertNotNull(pui);
+        assertFalse(pui.isUsedByOtherApps());
+        assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
+        assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
+    }
+
+    @Test
+    public void testNotifyPackageInstallSelfUse() {
+        TestData newPackage = new TestData("newPackage",
+                VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]), mUser0);
+
+        List<String> newSecondaries = newPackage.getSecondaryDexPaths();
+        // Packages should be able to find their own dex files even if the notification about
+        // their installation is delayed.
+        notifyDexLoad(newPackage, newSecondaries, mUser0);
+
+        PackageUseInfo pui = getPackageUseInfo(newPackage);
+        assertNotNull(pui);
+        assertFalse(pui.isUsedByOtherApps());
+        assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
+        assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
+    }
+
     private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
             List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
         for (String dex : secondaries) {