Make ImplInstanceManager a singleton

This moves the whole class into a singleton style for consistency.

Bug: 178730366
Test: presubmit
Change-Id: Idbba534fa81e80fcd865ebc056dd8d24a301e691
diff --git a/service/java/com/android/server/appsearch/AppSearchManagerService.java b/service/java/com/android/server/appsearch/AppSearchManagerService.java
index ed55f00..3bbc945 100644
--- a/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -64,7 +64,7 @@
     public void onStart() {
         publishBinderService(Context.APP_SEARCH_SERVICE, new Stub());
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
-        mImplInstanceManager = new ImplInstanceManager(getContext());
+        mImplInstanceManager = ImplInstanceManager.getInstance(getContext());
     }
 
     private class Stub extends IAppSearchManager.Stub {
@@ -102,7 +102,8 @@
                     }
                     schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
                 }
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 impl.setSchema(
                         packageName,
                         databaseName,
@@ -133,7 +134,8 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 List<AppSearchSchema> schemas = impl.getSchema(packageName, databaseName);
                 List<Bundle> schemaBundles = new ArrayList<>(schemas.size());
                 for (int i = 0; i < schemas.size(); i++) {
@@ -166,7 +168,8 @@
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 for (int i = 0; i < documentBundles.size(); i++) {
                     GenericDocument document = new GenericDocument(documentBundles.get(i));
                     try {
@@ -207,12 +210,18 @@
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
-                        GenericDocument document = impl.getDocument(packageName, databaseName,
-                                namespace, uri, typePropertyPaths);
+                        GenericDocument document =
+                                impl.getDocument(
+                                        packageName,
+                                        databaseName,
+                                        namespace,
+                                        uri,
+                                        typePropertyPaths);
                         resultBuilder.setSuccess(uri, document.getBundle());
                     } catch (Throwable t) {
                         resultBuilder.setResult(uri, throwableToFailedResult(t));
@@ -245,7 +254,8 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 SearchResultPage searchResultPage =
                         impl.query(
                                 packageName,
@@ -278,12 +288,14 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
-                SearchResultPage searchResultPage = impl.globalQuery(
-                        queryExpression,
-                        new SearchSpec(searchSpecBundle),
-                        packageName,
-                        callingUid);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                SearchResultPage searchResultPage =
+                        impl.globalQuery(
+                                queryExpression,
+                                new SearchSpec(searchSpecBundle),
+                                packageName,
+                                callingUid);
                 invokeCallbackOnResult(
                         callback,
                         AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
@@ -306,7 +318,8 @@
             // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally
             // opened it
             try {
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
                 invokeCallbackOnResult(
                         callback,
@@ -324,7 +337,8 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 impl.invalidateNextPageToken(nextPageToken);
             } catch (Throwable t) {
                 Log.e(TAG, "Unable to invalidate the query page token", t);
@@ -350,15 +364,11 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
-                impl.reportUsage(
-                        packageName,
-                        databaseName,
-                        namespace,
-                        uri,
-                        usageTimeMillis);
-                invokeCallbackOnResult(callback,
-                        AppSearchResult.newSuccessfulResult(/*result=*/ null));
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                impl.reportUsage(packageName, databaseName, namespace, uri, usageTimeMillis);
+                invokeCallbackOnResult(
+                        callback, AppSearchResult.newSuccessfulResult(/*result=*/ null));
             } catch (Throwable t) {
                 invokeCallbackOnError(callback, t);
             } finally {
@@ -385,7 +395,8 @@
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
@@ -421,7 +432,8 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 impl.removeByQuery(
                         packageName,
                         databaseName,
@@ -441,7 +453,8 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 impl.persistToDisk();
             } catch (Throwable t) {
                 Log.e(TAG, "Unable to persist the data to disk", t);
@@ -457,7 +470,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                mImplInstanceManager.getInstance(callingUserId);
+                mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
             } catch (Throwable t) {
                 invokeCallbackOnError(callback, t);
diff --git a/service/java/com/android/server/appsearch/ImplInstanceManager.java b/service/java/com/android/server/appsearch/ImplInstanceManager.java
index fe3c2e1..97b1a8c 100644
--- a/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -41,14 +41,33 @@
 public final class ImplInstanceManager {
     private static final String APP_SEARCH_DIR = "appSearch";
 
-    private static final SparseArray<AppSearchImpl> sInstances = new SparseArray<>();
+    private static ImplInstanceManager sImplInstanceManager;
 
-    private final Context mContext;
+    private final SparseArray<AppSearchImpl> mInstances = new SparseArray<>();
     private final String mGlobalQuerierPackage;
 
-    public ImplInstanceManager(@NonNull Context context) {
-        mContext = context;
-        mGlobalQuerierPackage = getGlobalAppSearchDataQuerierPackageName(mContext);
+    private ImplInstanceManager(@NonNull String globalQuerierPackage) {
+        mGlobalQuerierPackage = globalQuerierPackage;
+    }
+
+    /**
+     * Gets an instance of ImplInstanceManager to be used.
+     *
+     * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the
+     * existing instance will be returned.
+     */
+    @NonNull
+    public static ImplInstanceManager getInstance(@NonNull Context context) {
+        if (sImplInstanceManager == null) {
+            synchronized (ImplInstanceManager.class) {
+                if (sImplInstanceManager == null) {
+                    sImplInstanceManager =
+                            new ImplInstanceManager(
+                                    getGlobalAppSearchDataQuerierPackageName(context));
+                }
+            }
+        }
+        return sImplInstanceManager;
     }
 
     /**
@@ -57,30 +76,30 @@
      * <p>If no AppSearchImpl instance exists for this user, Icing will be initialized and one will
      * be created.
      *
+     * @param context The context
      * @param userId The multi-user userId of the device user calling AppSearch
      * @return An initialized {@link AppSearchImpl} for this user
      */
     @NonNull
-    public AppSearchImpl getInstance(@UserIdInt int userId)
+    public AppSearchImpl getAppSearchImpl(@NonNull Context context, @UserIdInt int userId)
             throws AppSearchException {
-        AppSearchImpl instance = sInstances.get(userId);
+        AppSearchImpl instance = mInstances.get(userId);
         if (instance == null) {
             synchronized (ImplInstanceManager.class) {
-                instance = sInstances.get(userId);
+                instance = mInstances.get(userId);
                 if (instance == null) {
-                    instance = createImpl(userId);
-                    sInstances.put(userId, instance);
+                    instance = createImpl(context, userId);
+                    mInstances.put(userId, instance);
                 }
             }
         }
         return instance;
     }
 
-    private AppSearchImpl createImpl(@UserIdInt int userId)
+    private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
             throws AppSearchException {
-        File appSearchDir = getAppSearchDir(mContext, userId);
-        return AppSearchImpl.create(
-                appSearchDir, mContext, userId, mGlobalQuerierPackage);
+        File appSearchDir = getAppSearchDir(context, userId);
+        return AppSearchImpl.create(appSearchDir, context, userId, mGlobalQuerierPackage);
     }
 
     private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
@@ -96,7 +115,8 @@
      *
      * @param context Context of the system service.
      */
-    private static String getGlobalAppSearchDataQuerierPackageName(Context context) {
+    @NonNull
+    private static String getGlobalAppSearchDataQuerierPackageName(@NonNull Context context) {
         String globalAppSearchDataQuerierPackage =
                 context.getString(R.string.config_globalAppSearchDataQuerierPackage);
         try {