Make sure always reuse ApplicationInfo when generating ProviderInfo

Bug: 148588589
Test: atest CtsContentTestCases (modulo preexisting failures)
Change-Id: If81f3b343fb018ba52127d038d02e62ca516c18f
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index e86a42c..f497f11 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -57,6 +57,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.server.IntentResolver;
 import com.android.server.pm.parsing.PackageInfoUtils;
+import com.android.server.pm.parsing.PackageInfoUtils.CachedApplicationInfoGenerator;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.io.PrintWriter;
@@ -273,9 +274,7 @@
             return null;
         }
         List<ProviderInfo> providerList = null;
-
-        // Map from a package name to the corresponding app info.
-        ArrayMap<String, ApplicationInfo> appInfos = null;
+        CachedApplicationInfoGenerator appInfoGenerator = null;
         synchronized (mLock) {
             for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) {
                 final ParsedProvider p = mProviders.mProviders.valueAt(i);
@@ -304,26 +303,15 @@
                         && (p.getMetaData() == null || !p.getMetaData().containsKey(metaDataKey))) {
                     continue;
                 }
-
-                // Make sure we have AppInfo for this provider.
-                final PackageUserState state = ps.readUserState(userId);
-                ApplicationInfo appInfo =
-                        (appInfos == null) ? null : appInfos.get(pkg.getPackageName());
-                if (appInfo == null) {
-                    appInfo = PackageInfoUtils.generateApplicationInfo(
-                            pkg, flags, state, userId, ps);
-                    if (appInfo == null) {
-                        // In this case, we should avoid calling generateApplicationInfo() for
-                        // the same package in subsequent iterations, but appInfo shouldn't be null
-                        // here, so we don't bother.
-                        continue;
-                    }
-                    if (appInfos == null) {
-                        appInfos = new ArrayMap<>(4);
-                    }
-                    appInfos.put(pkg.getPackageName(), appInfo);
+                if (appInfoGenerator == null) {
+                    appInfoGenerator = new CachedApplicationInfoGenerator();
                 }
-                // At this point, appInfo != null.
+                final PackageUserState state = ps.readUserState(userId);
+                final ApplicationInfo appInfo =
+                        appInfoGenerator.generate(pkg, flags, state, userId, ps);
+                if (appInfo == null) {
+                    continue;
+                }
 
                 final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
                         pkg, p, flags, state, appInfo, userId, ps);
@@ -355,14 +343,20 @@
             if (pkg == null) {
                 return null;
             }
-            return PackageInfoUtils.generateProviderInfo(pkg, p, flags,
-                    ps.readUserState(userId), userId, ps);
+            final PackageUserState state = ps.readUserState(userId);
+            ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+                    pkg, flags, state, userId, ps);
+            if (appInfo == null) {
+                return null;
+            }
+            return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, appInfo, userId, ps);
         }
     }
 
     void querySyncProviders(List<String> outNames, List<ProviderInfo> outInfo, boolean safeMode,
             int userId) {
         synchronized (mLock) {
+            CachedApplicationInfoGenerator appInfoGenerator = null;
             for (int i = mProvidersByAuthority.size() - 1; i >= 0; --i) {
                 final ParsedProvider p = mProvidersByAuthority.valueAt(i);
                 if (!p.isSyncable()) {
@@ -384,9 +378,18 @@
                 if (safeMode && !pkg.isSystem()) {
                     continue;
                 }
-                final ProviderInfo info =
-                        PackageInfoUtils.generateProviderInfo(pkg, p, 0,
-                                ps.readUserState(userId), userId, ps);
+                if (appInfoGenerator == null) {
+                    appInfoGenerator = new CachedApplicationInfoGenerator();
+                }
+                final PackageUserState state = ps.readUserState(userId);
+                final ApplicationInfo appInfo =
+                        appInfoGenerator.generate(pkg, 0, state, userId, ps);
+                if (appInfo == null) {
+                    continue;
+                }
+
+                final ProviderInfo info = PackageInfoUtils.generateProviderInfo(
+                        pkg, p, 0, state, appInfo, userId, ps);
                 if (info == null) {
                     continue;
                 }
@@ -1731,8 +1734,13 @@
             if (userState.instantApp && ps.isUpdateAvailable()) {
                 return null;
             }
+            final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+                    pkg, mFlags, userState, userId, ps);
+            if (appInfo == null) {
+                return null;
+            }
             ProviderInfo pi = PackageInfoUtils.generateProviderInfo(pkg, provider, mFlags,
-                    userState, userId, ps);
+                    userState, appInfo, userId, ps);
             if (pi == null) {
                 return null;
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index eb742e9..d84e920 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -5459,7 +5459,13 @@
                     return null;
                 }
                 PackageUserState state = ps.readUserState(userId);
-                return PackageInfoUtils.generateProviderInfo(pkg, p, flags, state, userId, ps);
+                final ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(
+                        pkg, flags, state, userId, ps);
+                if (appInfo == null) {
+                    return null;
+                }
+                return PackageInfoUtils.generateProviderInfo(
+                        pkg, p, flags, state, appInfo, userId, ps);
             }
         }
         return null;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index f5ce080..4ab1f396 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -48,7 +48,7 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.Pair;
+import android.util.Slog;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.server.pm.PackageSetting;
@@ -61,6 +61,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 
@@ -72,6 +73,7 @@
  * @hide
  **/
 public class PackageInfoUtils {
+    private static final String TAG = PackageParser2.TAG;
 
     /**
      * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
@@ -312,35 +314,22 @@
 
     /**
      * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
-     *
-     * @deprecated use {@link #generateProviderInfo(
-     * AndroidPackage, ParsedProvider, int, PackageUserState, ApplicationInfo, int, PackageSetting)}
-     * instead and pass {@link ApplicationInfo} explicitly to avoid generating duplicate instances
-     * of it.
-     */
-    @Nullable
-    @Deprecated
-    public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
-            @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId,
-            @Nullable PackageSetting pkgSetting) {
-        return generateProviderInfo(pkg, p, flags, state, null, userId, pkgSetting);
-    }
-
-    /**
-     * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage.
      */
     @Nullable
     public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p,
             @PackageManager.ComponentInfoFlags int flags, PackageUserState state,
-            @Nullable ApplicationInfo applicationInfo, int userId,
+            @NonNull ApplicationInfo applicationInfo, int userId,
             @Nullable PackageSetting pkgSetting) {
         if (p == null) return null;
+        if (applicationInfo == null || !pkg.getPackageName().equals(applicationInfo.packageName)) {
+            Slog.wtf(TAG, "AppInfo's package name is different. Expected=" + pkg.getPackageName()
+                    + " actual=" + (applicationInfo == null ? "(null AppInfo)"
+                    : applicationInfo.packageName));
+            applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
+        }
         if (!checkUseInstalledOrHidden(pkg, pkgSetting, state, flags)) {
             return null;
         }
-        if (applicationInfo == null) {
-            applicationInfo = generateApplicationInfo(pkg, flags, state, userId, pkgSetting);
-        }
         ProviderInfo info = PackageInfoWithoutStateUtils.generateProviderInfo(pkg, p, flags, state,
                 applicationInfo, userId);
         if (info == null) {
@@ -486,4 +475,29 @@
                 | flag(pkg.isSignedWithPlatformKey(), ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY);
         // @formatter:on
     }
+
+    /**
+     * Wraps {@link PackageInfoUtils#generateApplicationInfo} with a cache.
+     */
+    public static class CachedApplicationInfoGenerator {
+        // Map from a package name to the corresponding app info.
+        private ArrayMap<String, ApplicationInfo> mCache = new ArrayMap<>();
+
+        /**
+         * {@link PackageInfoUtils#generateApplicationInfo} with a cache.
+         */
+        @Nullable
+        public ApplicationInfo generate(AndroidPackage pkg,
+                @PackageManager.ApplicationInfoFlags int flags, PackageUserState state, int userId,
+                @Nullable PackageSetting pkgSetting) {
+            ApplicationInfo appInfo = mCache.get(pkg.getPackageName());
+            if (appInfo != null) {
+                return appInfo;
+            }
+            appInfo = PackageInfoUtils.generateApplicationInfo(
+                    pkg, flags, state, userId, pkgSetting);
+            mCache.put(pkg.getPackageName(), appInfo);
+            return appInfo;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index f99791a..d561b9c 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -42,7 +42,7 @@
  */
 public class PackageParser2 {
 
-    private static final String TAG = "PackageParser2";
+    static final String TAG = "PackageParser2";
 
     private static final boolean LOG_PARSE_TIMINGS = Build.IS_DEBUGGABLE;
     private static final int LOG_PARSE_TIMINGS_THRESHOLD_MS = 100;