Changes getMaxIndexableProperties to not need context

Test: testSetSchema_maxProperties
Bug: 301620635
Relnote: Remoes context param from getMaxIndexableProperties
Change-Id: I73204022669c75c33f5bb92d8888ccca049e8662
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java
index 557d214..642e306 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java
@@ -16,8 +16,6 @@
 // @exportToFramework:copyToPath(testing/testutils/src/android/app/appsearch/testutil/external/AlwaysSupportedFeatures.java)
 package androidx.appsearch.localstorage;
 
-import android.content.Context;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 import androidx.appsearch.app.Features;
@@ -78,7 +76,7 @@
     }
 
     @Override
-    public int getMaxIndexedProperties(@NonNull Context unused) {
+    public int getMaxIndexedProperties() {
         return 64;
     }
 }
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
index 4eb234c..4d7b3f3 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/FeaturesImpl.java
@@ -34,6 +34,17 @@
 final class FeaturesImpl implements Features {
     private static final String APPSEARCH_MODULE_NAME = "com.android.appsearch";
 
+    // This will be set to -1 to indicate the AppSearch version code hasn't bee checked, then to
+    // 0 if it is not found, or the version code if it is found.
+    private static volatile long sAppSearchVersionCode = -1;
+
+    // Context is used to check mainline module version, as support varies by module version.
+    private final Context mContext;
+
+    FeaturesImpl(@NonNull Context context) {
+        mContext = Preconditions.checkNotNull(context);
+    }
+
     @Override
     public boolean isFeatureSupported(@NonNull String feature) {
         switch (feature) {
@@ -92,32 +103,45 @@
     }
 
     @Override
-    public int getMaxIndexedProperties(@NonNull Context context) {
-        Preconditions.checkNotNull(context);
+    public int getMaxIndexedProperties() {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
             return 64;
         } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.TIRAMISU) {
-            PackageManager packageManager = context.getPackageManager();
-            long appsearchVersionCode = 0;
-            try {
-                String appSearchPackageName = ApiHelperForQ.getAppSearchPackageName(packageManager);
-                if (appSearchPackageName == null) {
-                    return 16;
-                }
-                PackageInfo pInfo = packageManager
-                        .getPackageInfo(appSearchPackageName, PackageManager.MATCH_APEX);
-                appsearchVersionCode = ApiHelperForQ.getPackageInfoLongVersionCode(pInfo);
-            } catch (PackageManager.NameNotFoundException e) {
-                // Module not installed
-            }
             // Sixty-four properties were enabled in mainline module 'aml_ase_331311020'
-            return appsearchVersionCode >= 331311020 ? 64 : 16;
+            return getAppSearchVersionCode(mContext) >= 331311020 ? 64 : 16;
         } else {
             return 16;
         }
     }
 
     @RequiresApi(Build.VERSION_CODES.Q)
+    private static long getAppSearchVersionCode(Context context) {
+        if (sAppSearchVersionCode != -1) {
+            return sAppSearchVersionCode;
+        }
+        synchronized (FeaturesImpl.class) {
+            // Check again in case it was assigned while waiting
+            if (sAppSearchVersionCode == -1) {
+                long appsearchVersionCode = 0;
+                try {
+                    PackageManager packageManager = context.getPackageManager();
+                    String appSearchPackageName =
+                            ApiHelperForQ.getAppSearchPackageName(packageManager);
+                    if (appSearchPackageName != null) {
+                        PackageInfo pInfo = packageManager
+                                .getPackageInfo(appSearchPackageName, PackageManager.MATCH_APEX);
+                        appsearchVersionCode = ApiHelperForQ.getPackageInfoLongVersionCode(pInfo);
+                    }
+                } catch (PackageManager.NameNotFoundException e) {
+                    // Module not installed
+                }
+                sAppSearchVersionCode = appsearchVersionCode;
+            }
+        }
+        return sAppSearchVersionCode;
+    }
+
+    @RequiresApi(Build.VERSION_CODES.Q)
     private static class ApiHelperForQ {
         @DoNotInline
         static long getPackageInfoLongVersionCode(PackageInfo pInfo) {
diff --git a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/PlatformStorage.java b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/PlatformStorage.java
index 1da86d9..c1ca5d4 100644
--- a/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/PlatformStorage.java
+++ b/appsearch/appsearch-platform-storage/src/main/java/androidx/appsearch/platformstorage/PlatformStorage.java
@@ -224,7 +224,7 @@
                     if (result.isSuccess()) {
                         future.set(
                                 new SearchSessionImpl(result.getResultValue(), context.mExecutor,
-                                        new FeaturesImpl()));
+                                        new FeaturesImpl(context.mContext)));
                     } else {
                         // Without the SuppressLint annotation on the method, this line causes a
                         // lint error because getResultCode isn't defined as returning a value from
@@ -254,7 +254,7 @@
                     if (result.isSuccess()) {
                         future.set(new GlobalSearchSessionImpl(
                                 result.getResultValue(), context.mExecutor,
-                                new FeaturesImpl()));
+                                new FeaturesImpl(context.mContext)));
                     } else {
                         // Without the SuppressLint annotation on the method, this line causes a
                         // lint error because getResultCode isn't defined as returning a value from
diff --git a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/FeaturesImpl.java b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/FeaturesImpl.java
index bef55fa..2f580b9 100644
--- a/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/FeaturesImpl.java
+++ b/appsearch/appsearch-play-services-storage/src/main/java/androidx/appsearch/playservicesstorage/FeaturesImpl.java
@@ -16,8 +16,6 @@
 
 package androidx.appsearch.playservicesstorage;
 
-import android.content.Context;
-
 import androidx.annotation.NonNull;
 import androidx.appsearch.app.Features;
 
@@ -107,7 +105,7 @@
         }
     }
     @Override
-    public int getMaxIndexedProperties(@NonNull Context unused) {
+    public int getMaxIndexedProperties() {
         // TODO(b/241310816): Update to reflect support in Android U+ once 64 indexable properties
         //  are possible in service-appsearch.
         return 16;
diff --git a/appsearch/appsearch/api/current.txt b/appsearch/appsearch/api/current.txt
index 4e448cd..be79641 100644
--- a/appsearch/appsearch/api/current.txt
+++ b/appsearch/appsearch/api/current.txt
@@ -246,7 +246,7 @@
   }
 
   public interface Features {
-    method public int getMaxIndexedProperties(android.content.Context);
+    method public int getMaxIndexedProperties();
     method public boolean isFeatureSupported(String);
     field public static final String ADD_PERMISSIONS_AND_GET_VISIBILITY = "ADD_PERMISSIONS_AND_GET_VISIBILITY";
     field public static final String GLOBAL_SEARCH_SESSION_GET_BY_ID = "GLOBAL_SEARCH_SESSION_GET_BY_ID";
diff --git a/appsearch/appsearch/api/restricted_current.txt b/appsearch/appsearch/api/restricted_current.txt
index 4e448cd..be79641 100644
--- a/appsearch/appsearch/api/restricted_current.txt
+++ b/appsearch/appsearch/api/restricted_current.txt
@@ -246,7 +246,7 @@
   }
 
   public interface Features {
-    method public int getMaxIndexedProperties(android.content.Context);
+    method public int getMaxIndexedProperties();
     method public boolean isFeatureSupported(String);
     field public static final String ADD_PERMISSIONS_AND_GET_VISIBILITY = "ADD_PERMISSIONS_AND_GET_VISIBILITY";
     field public static final String GLOBAL_SEARCH_SESSION_GET_BY_ID = "GLOBAL_SEARCH_SESSION_GET_BY_ID";
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
index 1903f7c..672c0f3 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/cts/app/AppSearchSessionCtsTestBase.java
@@ -472,8 +472,7 @@
     /** Test indexing maximum properties into a schema. */
     @Test
     public void testSetSchema_maxProperties() throws Exception {
-        int maxProperties = mDb1.getFeatures().getMaxIndexedProperties(
-                ApplicationProvider.getApplicationContext());
+        int maxProperties = mDb1.getFeatures().getMaxIndexedProperties();
         AppSearchSchema.Builder schemaBuilder = new AppSearchSchema.Builder("testSchema");
         for (int i = 0; i < maxProperties; i++) {
             schemaBuilder.addProperty(new StringPropertyConfig.Builder("string" + i)
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java
index e62622d..e81c57d 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java
@@ -15,8 +15,6 @@
  */
 package androidx.appsearch.app;
 
-import android.content.Context;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.RestrictTo;
 
@@ -190,7 +188,6 @@
      * given the Android API level and AppSearch backend.
      *
      * <p>A property is defined as all values that are present at a particular path.
-     * @param context to check mainline module version, as support varies by module version.
      */
-    int getMaxIndexedProperties(@NonNull Context context);
+    int getMaxIndexedProperties();
 }