Loading apex/appsearch/framework/java/android/app/appsearch/AppSearchDocument.java +5 −1 Original line number Diff line number Diff line Loading @@ -47,6 +47,10 @@ import java.util.Objects; public class AppSearchDocument { private static final String TAG = "AppSearchDocument"; /** The default empty namespace.*/ // TODO(adorokhine): Allow namespace to be specified in the document. public static final String DEFAULT_NAMESPACE = ""; /** * The maximum number of elements in a repeatable field. Will reject the request if exceed * this limit. Loading Loading @@ -450,7 +454,7 @@ public class AppSearchDocument { */ public Builder(@NonNull String uri, @NonNull String schemaType) { mBuilderTypeInstance = (BuilderType) this; mProtoBuilder.setUri(uri).setSchema(schemaType); mProtoBuilder.setUri(uri).setSchema(schemaType).setNamespace(DEFAULT_NAMESPACE); // Set current timestamp for creation timestamp by default. setCreationTimestampMillis(System.currentTimeMillis()); } Loading apex/appsearch/service/java/com/android/server/appsearch/AppSearchException.java→apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java +15 −8 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. Loading @@ -14,31 +14,38 @@ * limitations under the License. */ package com.android.server.appsearch; package android.app.appsearch.exceptions; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.appsearch.AppSearchResult; /** * An exception thrown by {@link com.android.server.appsearch.AppSearchManagerService} or a * subcomponent. * An exception thrown by {@code android.app.appsearch.AppSearchManager} or a subcomponent. * * <p>These exceptions can be converted into a failed {@link android.app.appsearch.AppSearchResult} * <p>These exceptions can be converted into a failed {@link AppSearchResult} * for propagating to the client. * @hide */ //TODO(b/157082794): Linkify to AppSearchManager once that API is public public class AppSearchException extends Exception { private final @AppSearchResult.ResultCode int mResultCode; /** Initializes an {@link com.android.server.appsearch.AppSearchException} with no message. */ /** * Initializes an {@link AppSearchException} with no message. * @hide */ public AppSearchException(@AppSearchResult.ResultCode int resultCode) { this(resultCode, /*message=*/ null); } /** @hide */ public AppSearchException( @AppSearchResult.ResultCode int resultCode, @Nullable String message) { this(resultCode, message, /*cause=*/ null); } /** @hide */ public AppSearchException( @AppSearchResult.ResultCode int resultCode, @Nullable String message, Loading @@ -48,9 +55,9 @@ public class AppSearchException extends Exception { } /** * Converts this {@link java.lang.Exception} into a failed * {@link android.app.appsearch.AppSearchResult} * Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult} */ @NonNull public <T> AppSearchResult<T> toAppSearchResult() { return AppSearchResult.newFailedResult(mResultCode, getMessage()); } Loading apex/appsearch/service/Android.bp +7 −1 Original line number Diff line number Diff line Loading @@ -20,7 +20,13 @@ java_library { "framework-appsearch", "services.core", ], static_libs: ["icing-java-proto-lite"], static_libs: [ "icing-java-proto-lite", "libicing-java", ], required: [ "libicing", ], jarjar_rules: "jarjar-rules.txt", apex_available: ["com.android.appsearch"], } apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +41 −22 Original line number Diff line number Diff line Loading @@ -17,8 +17,10 @@ package com.android.server.appsearch; import android.annotation.NonNull; import android.app.appsearch.AppSearchBatchResult; import android.app.appsearch.AppSearchDocument; import android.app.appsearch.AppSearchResult; import android.app.appsearch.IAppSearchManager; import android.app.appsearch.exceptions.AppSearchException; import android.content.Context; import android.os.Binder; import android.os.UserHandle; Loading @@ -26,8 +28,7 @@ import android.os.UserHandle; import com.android.internal.infra.AndroidFuture; import com.android.internal.util.Preconditions; import com.android.server.SystemService; import com.android.server.appsearch.impl.AppSearchImpl; import com.android.server.appsearch.impl.ImplInstanceManager; import com.android.server.appsearch.external.localbackend.AppSearchImpl; import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.ResultSpecProto; Loading @@ -44,6 +45,7 @@ import java.util.List; * TODO(b/142567528): add comments when implement this class */ public class AppSearchManagerService extends SystemService { private static final String TAG = "AppSearchManagerService"; public AppSearchManagerService(Context context) { super(context); Loading @@ -68,7 +70,8 @@ public class AppSearchManagerService extends SystemService { try { SchemaProto schema = SchemaProto.parseFrom(schemaBytes); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); impl.setSchema(callingUid, schema, forceOverride); String databaseName = makeDatabaseName(callingUid); impl.setSchema(databaseName, schema, forceOverride); callback.complete(AppSearchResult.newSuccessfulResult(/*value=*/ null)); } catch (Throwable t) { callback.complete(throwableToFailedResult(t)); Loading @@ -88,13 +91,14 @@ public class AppSearchManagerService extends SystemService { long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); String databaseName = makeDatabaseName(callingUid); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); for (int i = 0; i < documentsBytes.size(); i++) { byte[] documentBytes = (byte[]) documentsBytes.get(i); DocumentProto document = DocumentProto.parseFrom(documentBytes); try { impl.putDocument(callingUid, document); impl.putDocument(databaseName, document); resultBuilder.setSuccess(document.getUri(), /*value=*/ null); } catch (Throwable t) { resultBuilder.setResult(document.getUri(), throwableToFailedResult(t)); Loading @@ -118,12 +122,14 @@ public class AppSearchManagerService extends SystemService { long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); String databaseName = makeDatabaseName(callingUid); AppSearchBatchResult.Builder<String, byte[]> resultBuilder = new AppSearchBatchResult.Builder<>(); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { DocumentProto document = impl.getDocument(callingUid, uri); DocumentProto document = impl.getDocument( databaseName, AppSearchDocument.DEFAULT_NAMESPACE, uri); if (document == null) { resultBuilder.setFailure( uri, AppSearchResult.RESULT_NOT_FOUND, /*errorMessage=*/ null); Loading Loading @@ -161,8 +167,9 @@ public class AppSearchManagerService extends SystemService { ResultSpecProto resultSpecProto = ResultSpecProto.parseFrom(resultSpecBytes); ScoringSpecProto scoringSpecProto = ScoringSpecProto.parseFrom(scoringSpecBytes); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); SearchResultProto searchResultProto = impl.query(callingUid, searchSpecProto, resultSpecProto, scoringSpecProto); String databaseName = makeDatabaseName(callingUid); SearchResultProto searchResultProto = impl.query( databaseName, searchSpecProto, resultSpecProto, scoringSpecProto); // TODO(sidchhabra): Translate SearchResultProto errors into error codes. This might // better be done in AppSearchImpl by throwing an AppSearchException. if (searchResultProto.getStatus().getCode() != StatusProto.Code.OK) { Loading Loading @@ -190,17 +197,14 @@ public class AppSearchManagerService extends SystemService { long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); String databaseName = makeDatabaseName(callingUid); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { if (!impl.delete(callingUid, uri)) { resultBuilder.setFailure( uri, AppSearchResult.RESULT_NOT_FOUND, /*errorMessage=*/ null); } else { impl.remove(databaseName, AppSearchDocument.DEFAULT_NAMESPACE, uri); resultBuilder.setSuccess(uri, /*value= */null); } } catch (Throwable t) { resultBuilder.setResult(uri, throwableToFailedResult(t)); } Loading @@ -223,19 +227,14 @@ public class AppSearchManagerService extends SystemService { long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); String databaseName = makeDatabaseName(callingUid); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); for (int i = 0; i < schemaTypes.size(); i++) { String schemaType = schemaTypes.get(i); try { if (!impl.deleteByType(callingUid, schemaType)) { resultBuilder.setFailure( schemaType, AppSearchResult.RESULT_NOT_FOUND, /*errorMessage=*/ null); } else { impl.removeByType(databaseName, schemaType); resultBuilder.setSuccess(schemaType, /*value=*/ null); } } catch (Throwable t) { resultBuilder.setResult(schemaType, throwableToFailedResult(t)); } Loading @@ -256,7 +255,8 @@ public class AppSearchManagerService extends SystemService { long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); impl.deleteAll(callingUid); String databaseName = makeDatabaseName(callingUid); impl.removeAll(databaseName); callback.complete(AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { callback.complete(throwableToFailedResult(t)); Loading @@ -265,6 +265,25 @@ public class AppSearchManagerService extends SystemService { } } /** * Returns a unique database name for the given uid. * * <p>The current implementation returns the package name of the app with this uid in a * format like {@code com.example.package} or {@code com.example.sharedname:5678}. */ @NonNull private String makeDatabaseName(int callingUid) { // For regular apps, this call will return the package name. If callingUid is an // android:sharedUserId, this value may be another type of name and have a :uid suffix. String callingUidName = getContext().getPackageManager().getNameForUid(callingUid); if (callingUidName == null) { // Not sure how this is possible --- maybe app was uninstalled? throw new IllegalStateException( "Failed to look up package name for uid " + callingUid); } return callingUidName; } private <ValueType> AppSearchResult<ValueType> throwableToFailedResult( @NonNull Throwable t) { if (t instanceof AppSearchException) { Loading apex/appsearch/service/java/com/android/server/appsearch/impl/ImplInstanceManager.java→apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +30 −3 Original line number Diff line number Diff line Loading @@ -14,21 +14,32 @@ * limitations under the License. */ package com.android.server.appsearch.impl; package com.android.server.appsearch; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.appsearch.exceptions.AppSearchException; import android.content.Context; import android.os.Environment; import android.os.storage.StorageManager; import android.util.SparseArray; import com.android.server.appsearch.external.localbackend.AppSearchImpl; import java.io.File; /** * Manages the lifecycle of instances of {@link AppSearchImpl}. * * <p>These instances are managed per unique device-user. */ public final class ImplInstanceManager { private static final String APP_SEARCH_DIR = "appSearch"; private static final SparseArray<AppSearchImpl> sInstances = new SparseArray<>(); private ImplInstanceManager() {} /** * Gets an instance of AppSearchImpl for the given user. * Loading @@ -40,17 +51,33 @@ public final class ImplInstanceManager { * @return An initialized {@link AppSearchImpl} for this user */ @NonNull public static AppSearchImpl getInstance(@NonNull Context context, @UserIdInt int userId) { public static AppSearchImpl getInstance(@NonNull Context context, @UserIdInt int userId) throws AppSearchException { AppSearchImpl instance = sInstances.get(userId); if (instance == null) { synchronized (ImplInstanceManager.class) { instance = sInstances.get(userId); if (instance == null) { instance = new AppSearchImpl(context, userId); instance = createImpl(context, userId); sInstances.put(userId, instance); } } } return instance; } private static AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId) throws AppSearchException { File appSearchDir = getAppSearchDir(context, userId); AppSearchImpl appSearchImpl = new AppSearchImpl(appSearchDir); appSearchImpl.initialize(); return appSearchImpl; } private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) { // See com.android.internal.app.ChooserActivity::getPinnedSharedPrefs File userCeDir = Environment.getDataUserCePackageDirectory( StorageManager.UUID_PRIVATE_INTERNAL, userId, context.getPackageName()); return new File(userCeDir, APP_SEARCH_DIR); } } Loading
apex/appsearch/framework/java/android/app/appsearch/AppSearchDocument.java +5 −1 Original line number Diff line number Diff line Loading @@ -47,6 +47,10 @@ import java.util.Objects; public class AppSearchDocument { private static final String TAG = "AppSearchDocument"; /** The default empty namespace.*/ // TODO(adorokhine): Allow namespace to be specified in the document. public static final String DEFAULT_NAMESPACE = ""; /** * The maximum number of elements in a repeatable field. Will reject the request if exceed * this limit. Loading Loading @@ -450,7 +454,7 @@ public class AppSearchDocument { */ public Builder(@NonNull String uri, @NonNull String schemaType) { mBuilderTypeInstance = (BuilderType) this; mProtoBuilder.setUri(uri).setSchema(schemaType); mProtoBuilder.setUri(uri).setSchema(schemaType).setNamespace(DEFAULT_NAMESPACE); // Set current timestamp for creation timestamp by default. setCreationTimestampMillis(System.currentTimeMillis()); } Loading
apex/appsearch/service/java/com/android/server/appsearch/AppSearchException.java→apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java +15 −8 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. Loading @@ -14,31 +14,38 @@ * limitations under the License. */ package com.android.server.appsearch; package android.app.appsearch.exceptions; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.appsearch.AppSearchResult; /** * An exception thrown by {@link com.android.server.appsearch.AppSearchManagerService} or a * subcomponent. * An exception thrown by {@code android.app.appsearch.AppSearchManager} or a subcomponent. * * <p>These exceptions can be converted into a failed {@link android.app.appsearch.AppSearchResult} * <p>These exceptions can be converted into a failed {@link AppSearchResult} * for propagating to the client. * @hide */ //TODO(b/157082794): Linkify to AppSearchManager once that API is public public class AppSearchException extends Exception { private final @AppSearchResult.ResultCode int mResultCode; /** Initializes an {@link com.android.server.appsearch.AppSearchException} with no message. */ /** * Initializes an {@link AppSearchException} with no message. * @hide */ public AppSearchException(@AppSearchResult.ResultCode int resultCode) { this(resultCode, /*message=*/ null); } /** @hide */ public AppSearchException( @AppSearchResult.ResultCode int resultCode, @Nullable String message) { this(resultCode, message, /*cause=*/ null); } /** @hide */ public AppSearchException( @AppSearchResult.ResultCode int resultCode, @Nullable String message, Loading @@ -48,9 +55,9 @@ public class AppSearchException extends Exception { } /** * Converts this {@link java.lang.Exception} into a failed * {@link android.app.appsearch.AppSearchResult} * Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult} */ @NonNull public <T> AppSearchResult<T> toAppSearchResult() { return AppSearchResult.newFailedResult(mResultCode, getMessage()); } Loading
apex/appsearch/service/Android.bp +7 −1 Original line number Diff line number Diff line Loading @@ -20,7 +20,13 @@ java_library { "framework-appsearch", "services.core", ], static_libs: ["icing-java-proto-lite"], static_libs: [ "icing-java-proto-lite", "libicing-java", ], required: [ "libicing", ], jarjar_rules: "jarjar-rules.txt", apex_available: ["com.android.appsearch"], }
apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +41 −22 Original line number Diff line number Diff line Loading @@ -17,8 +17,10 @@ package com.android.server.appsearch; import android.annotation.NonNull; import android.app.appsearch.AppSearchBatchResult; import android.app.appsearch.AppSearchDocument; import android.app.appsearch.AppSearchResult; import android.app.appsearch.IAppSearchManager; import android.app.appsearch.exceptions.AppSearchException; import android.content.Context; import android.os.Binder; import android.os.UserHandle; Loading @@ -26,8 +28,7 @@ import android.os.UserHandle; import com.android.internal.infra.AndroidFuture; import com.android.internal.util.Preconditions; import com.android.server.SystemService; import com.android.server.appsearch.impl.AppSearchImpl; import com.android.server.appsearch.impl.ImplInstanceManager; import com.android.server.appsearch.external.localbackend.AppSearchImpl; import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.ResultSpecProto; Loading @@ -44,6 +45,7 @@ import java.util.List; * TODO(b/142567528): add comments when implement this class */ public class AppSearchManagerService extends SystemService { private static final String TAG = "AppSearchManagerService"; public AppSearchManagerService(Context context) { super(context); Loading @@ -68,7 +70,8 @@ public class AppSearchManagerService extends SystemService { try { SchemaProto schema = SchemaProto.parseFrom(schemaBytes); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); impl.setSchema(callingUid, schema, forceOverride); String databaseName = makeDatabaseName(callingUid); impl.setSchema(databaseName, schema, forceOverride); callback.complete(AppSearchResult.newSuccessfulResult(/*value=*/ null)); } catch (Throwable t) { callback.complete(throwableToFailedResult(t)); Loading @@ -88,13 +91,14 @@ public class AppSearchManagerService extends SystemService { long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); String databaseName = makeDatabaseName(callingUid); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); for (int i = 0; i < documentsBytes.size(); i++) { byte[] documentBytes = (byte[]) documentsBytes.get(i); DocumentProto document = DocumentProto.parseFrom(documentBytes); try { impl.putDocument(callingUid, document); impl.putDocument(databaseName, document); resultBuilder.setSuccess(document.getUri(), /*value=*/ null); } catch (Throwable t) { resultBuilder.setResult(document.getUri(), throwableToFailedResult(t)); Loading @@ -118,12 +122,14 @@ public class AppSearchManagerService extends SystemService { long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); String databaseName = makeDatabaseName(callingUid); AppSearchBatchResult.Builder<String, byte[]> resultBuilder = new AppSearchBatchResult.Builder<>(); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { DocumentProto document = impl.getDocument(callingUid, uri); DocumentProto document = impl.getDocument( databaseName, AppSearchDocument.DEFAULT_NAMESPACE, uri); if (document == null) { resultBuilder.setFailure( uri, AppSearchResult.RESULT_NOT_FOUND, /*errorMessage=*/ null); Loading Loading @@ -161,8 +167,9 @@ public class AppSearchManagerService extends SystemService { ResultSpecProto resultSpecProto = ResultSpecProto.parseFrom(resultSpecBytes); ScoringSpecProto scoringSpecProto = ScoringSpecProto.parseFrom(scoringSpecBytes); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); SearchResultProto searchResultProto = impl.query(callingUid, searchSpecProto, resultSpecProto, scoringSpecProto); String databaseName = makeDatabaseName(callingUid); SearchResultProto searchResultProto = impl.query( databaseName, searchSpecProto, resultSpecProto, scoringSpecProto); // TODO(sidchhabra): Translate SearchResultProto errors into error codes. This might // better be done in AppSearchImpl by throwing an AppSearchException. if (searchResultProto.getStatus().getCode() != StatusProto.Code.OK) { Loading Loading @@ -190,17 +197,14 @@ public class AppSearchManagerService extends SystemService { long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); String databaseName = makeDatabaseName(callingUid); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { if (!impl.delete(callingUid, uri)) { resultBuilder.setFailure( uri, AppSearchResult.RESULT_NOT_FOUND, /*errorMessage=*/ null); } else { impl.remove(databaseName, AppSearchDocument.DEFAULT_NAMESPACE, uri); resultBuilder.setSuccess(uri, /*value= */null); } } catch (Throwable t) { resultBuilder.setResult(uri, throwableToFailedResult(t)); } Loading @@ -223,19 +227,14 @@ public class AppSearchManagerService extends SystemService { long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); String databaseName = makeDatabaseName(callingUid); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); for (int i = 0; i < schemaTypes.size(); i++) { String schemaType = schemaTypes.get(i); try { if (!impl.deleteByType(callingUid, schemaType)) { resultBuilder.setFailure( schemaType, AppSearchResult.RESULT_NOT_FOUND, /*errorMessage=*/ null); } else { impl.removeByType(databaseName, schemaType); resultBuilder.setSuccess(schemaType, /*value=*/ null); } } catch (Throwable t) { resultBuilder.setResult(schemaType, throwableToFailedResult(t)); } Loading @@ -256,7 +255,8 @@ public class AppSearchManagerService extends SystemService { long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); impl.deleteAll(callingUid); String databaseName = makeDatabaseName(callingUid); impl.removeAll(databaseName); callback.complete(AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { callback.complete(throwableToFailedResult(t)); Loading @@ -265,6 +265,25 @@ public class AppSearchManagerService extends SystemService { } } /** * Returns a unique database name for the given uid. * * <p>The current implementation returns the package name of the app with this uid in a * format like {@code com.example.package} or {@code com.example.sharedname:5678}. */ @NonNull private String makeDatabaseName(int callingUid) { // For regular apps, this call will return the package name. If callingUid is an // android:sharedUserId, this value may be another type of name and have a :uid suffix. String callingUidName = getContext().getPackageManager().getNameForUid(callingUid); if (callingUidName == null) { // Not sure how this is possible --- maybe app was uninstalled? throw new IllegalStateException( "Failed to look up package name for uid " + callingUid); } return callingUidName; } private <ValueType> AppSearchResult<ValueType> throwableToFailedResult( @NonNull Throwable t) { if (t instanceof AppSearchException) { Loading
apex/appsearch/service/java/com/android/server/appsearch/impl/ImplInstanceManager.java→apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +30 −3 Original line number Diff line number Diff line Loading @@ -14,21 +14,32 @@ * limitations under the License. */ package com.android.server.appsearch.impl; package com.android.server.appsearch; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.appsearch.exceptions.AppSearchException; import android.content.Context; import android.os.Environment; import android.os.storage.StorageManager; import android.util.SparseArray; import com.android.server.appsearch.external.localbackend.AppSearchImpl; import java.io.File; /** * Manages the lifecycle of instances of {@link AppSearchImpl}. * * <p>These instances are managed per unique device-user. */ public final class ImplInstanceManager { private static final String APP_SEARCH_DIR = "appSearch"; private static final SparseArray<AppSearchImpl> sInstances = new SparseArray<>(); private ImplInstanceManager() {} /** * Gets an instance of AppSearchImpl for the given user. * Loading @@ -40,17 +51,33 @@ public final class ImplInstanceManager { * @return An initialized {@link AppSearchImpl} for this user */ @NonNull public static AppSearchImpl getInstance(@NonNull Context context, @UserIdInt int userId) { public static AppSearchImpl getInstance(@NonNull Context context, @UserIdInt int userId) throws AppSearchException { AppSearchImpl instance = sInstances.get(userId); if (instance == null) { synchronized (ImplInstanceManager.class) { instance = sInstances.get(userId); if (instance == null) { instance = new AppSearchImpl(context, userId); instance = createImpl(context, userId); sInstances.put(userId, instance); } } } return instance; } private static AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId) throws AppSearchException { File appSearchDir = getAppSearchDir(context, userId); AppSearchImpl appSearchImpl = new AppSearchImpl(appSearchDir); appSearchImpl.initialize(); return appSearchImpl; } private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) { // See com.android.internal.app.ChooserActivity::getPinnedSharedPrefs File userCeDir = Environment.getDataUserCePackageDirectory( StorageManager.UUID_PRIVATE_INTERNAL, userId, context.getPackageName()); return new File(userCeDir, APP_SEARCH_DIR); } }