Change the resource cache key and limit the maximum size of cache

Change the key valus of ResoucesCache. Key contains package name and
configuration of resources.
Even the same context object can return different resources depending
on its state, such as language changes. So a Configuration that can
logically distinguish whether a resource is logically equal can be
used as a key. (context does not have localical equals for resources)
Also, since unique resources can be used for each package, package
name is added as a resource cache key.

Bug: 289045241
Test: atest CtsTelephonyTestCases
Test: atest CellBroadcastServiceTests
Test: atest GoogleCellBroadcastReceiverUnitTests
Test: manually perform telephony tests and sanity checks
Change-Id: Iacd2b350a4f32d52aae63038303413c0768185fa
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 64c2a4c..baacb57 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -85,11 +85,11 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
-import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
@@ -1328,10 +1328,24 @@
 
     private final Context mContext;
 
-    // Cache of Resource that has been created in getResourcesForSubId. Key is a Pair containing
-    // the Context and subId.
-    private static final Map<Pair<Context, Integer>, Resources> sResourcesCache =
-            new ConcurrentHashMap<>();
+    /**
+     * In order to prevent the overflow of the heap size due to an indiscriminate increase in the
+     * cache, the heap size of the resource cache is set sufficiently large.
+     */
+    private static final int MAX_RESOURCE_CACHE_ENTRY_COUNT = 10_000;
+
+    /**
+     * Cache of Resources that has been created in getResourcesForSubId. Key contains package name,
+     * and Configuration of Resources. If more than the maximum number of resources are stored in
+     * this cache, the least recently used Resources will be removed to maintain the maximum size.
+     */
+    private static final Map<Pair<String, Configuration>, Resources> sResourcesCache =
+            Collections.synchronizedMap(new LinkedHashMap<>(16, 0.75f, true) {
+                @Override
+                protected boolean removeEldestEntry(Entry eldest) {
+                    return size() > MAX_RESOURCE_CACHE_ENTRY_COUNT;
+                }
+            });
 
     /**
      * A listener class for monitoring changes to {@link SubscriptionInfo} records.
@@ -2817,14 +2831,20 @@
     @NonNull
     public static Resources getResourcesForSubId(Context context, int subId,
             boolean useRootLocale) {
-        // Check if resources for this context and subId already exist in the resource cache.
-        // Resources that use the root locale are not cached.
-        Pair<Context, Integer> cacheKey = null;
-        if (isValidSubscriptionId(subId) && !useRootLocale) {
-            cacheKey = Pair.create(context, subId);
-            if (sResourcesCache.containsKey(cacheKey)) {
+        // Check if the Resources already exists in the cache based on the given context. Find a
+        // Resource that match Configuration.
+        Pair<String, Configuration> cacheKey = null;
+        if (isValidSubscriptionId(subId)) {
+            Configuration configurationKey =
+                    new Configuration(context.getResources().getConfiguration());
+            if (useRootLocale) {
+                configurationKey.setLocale(Locale.ROOT);
+            }
+            cacheKey = Pair.create(context.getPackageName(), configurationKey);
+            Resources cached = sResourcesCache.get(cacheKey);
+            if (cached != null) {
                 // Cache hit. Use cached Resources.
-                return sResourcesCache.get(cacheKey);
+                return cached;
             }
         }