summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Oluwarotimi Adesina <oadesina@google.com> 2024-09-09 10:28:46 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-09-09 10:28:46 +0000
commitefde44764cd00b995a42242e6e50eb7002b898b5 (patch)
tree6b6427f4238b23d8cdb537e5f8a3424951963979
parent29d12d84bd5513303f0fa837dc3ad8650345c608 (diff)
parentbca640f6ef969ae460390ff7993181a988169a18 (diff)
Merge "Introduce FutureGlobalSearchSession" into main
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java66
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java94
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java1
-rw-r--r--services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java1
-rw-r--r--services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt2
-rw-r--r--services/tests/appfunctions/src/com/android/server/appfunctions/FutureGlobalSearchSessionTest.kt141
6 files changed, 266 insertions, 39 deletions
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
index eba628dc1fba..03dd5dd62e51 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
@@ -16,9 +16,6 @@
package com.android.server.appfunctions;
-import static android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER;
-
-import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.app.appsearch.AppSearchBatchResult;
import android.app.appsearch.AppSearchManager;
@@ -42,10 +39,7 @@ import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
-/**
- * A future API wrapper of {@link AppSearchSession} APIs.
- */
-@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
+/** A future API wrapper of {@link AppSearchSession} APIs. */
public class FutureAppSearchSession implements Closeable {
private static final String TAG = FutureAppSearchSession.class.getSimpleName();
private final Executor mExecutor;
@@ -67,14 +61,14 @@ public class FutureAppSearchSession implements Closeable {
/** Converts a failed app search result codes into an exception. */
@NonNull
- private static Exception failedResultToException(@NonNull AppSearchResult<?> appSearchResult) {
+ public static Exception failedResultToException(@NonNull AppSearchResult<?> appSearchResult) {
return switch (appSearchResult.getResultCode()) {
- case AppSearchResult.RESULT_INVALID_ARGUMENT -> new IllegalArgumentException(
- appSearchResult.getErrorMessage());
- case AppSearchResult.RESULT_IO_ERROR -> new IOException(
- appSearchResult.getErrorMessage());
- case AppSearchResult.RESULT_SECURITY_ERROR -> new SecurityException(
- appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_INVALID_ARGUMENT ->
+ new IllegalArgumentException(appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_IO_ERROR ->
+ new IOException(appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_SECURITY_ERROR ->
+ new SecurityException(appSearchResult.getErrorMessage());
default -> new IllegalStateException(appSearchResult.getErrorMessage());
};
}
@@ -137,14 +131,16 @@ public class FutureAppSearchSession implements Closeable {
/** Indexes documents into the AppSearchSession database. */
public AndroidFuture<AppSearchBatchResult<String, Void>> put(
@NonNull PutDocumentsRequest putDocumentsRequest) {
- return getSessionAsync().thenCompose(
- session -> {
- AndroidFuture<AppSearchBatchResult<String, Void>> batchResultFuture =
- new AndroidFuture<>();
+ return getSessionAsync()
+ .thenCompose(
+ session -> {
+ AndroidFuture<AppSearchBatchResult<String, Void>> batchResultFuture =
+ new AndroidFuture<>();
- session.put(putDocumentsRequest, mExecutor, batchResultFuture::complete);
- return batchResultFuture;
- });
+ session.put(
+ putDocumentsRequest, mExecutor, batchResultFuture::complete);
+ return batchResultFuture;
+ });
}
/**
@@ -152,10 +148,9 @@ public class FutureAppSearchSession implements Closeable {
* of search provided.
*/
public AndroidFuture<FutureSearchResults> search(
- @NonNull String queryExpression,
- @NonNull SearchSpec searchSpec) {
- return getSessionAsync().thenApply(
- session -> session.search(queryExpression, searchSpec))
+ @NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
+ return getSessionAsync()
+ .thenApply(session -> session.search(queryExpression, searchSpec))
.thenApply(result -> new FutureSearchResults(result, mExecutor));
}
@@ -173,8 +168,8 @@ public class FutureAppSearchSession implements Closeable {
private final SearchResults mSearchResults;
private final Executor mExecutor;
- public FutureSearchResults(@NonNull SearchResults searchResults,
- @NonNull Executor executor) {
+ public FutureSearchResults(
+ @NonNull SearchResults searchResults, @NonNull Executor executor) {
mSearchResults = Objects.requireNonNull(searchResults);
mExecutor = Objects.requireNonNull(executor);
}
@@ -184,15 +179,14 @@ public class FutureAppSearchSession implements Closeable {
new AndroidFuture<>();
mSearchResults.getNextPage(mExecutor, nextPageFuture::complete);
- return nextPageFuture.thenApply(result -> {
- if (result.isSuccess()) {
- return result.getResultValue();
- } else {
- throw new RuntimeException(
- failedResultToException(result));
- }
- });
+ return nextPageFuture.thenApply(
+ result -> {
+ if (result.isSuccess()) {
+ return result.getResultValue();
+ } else {
+ throw new RuntimeException(failedResultToException(result));
+ }
+ });
}
-
}
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java b/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java
new file mode 100644
index 000000000000..0c2262456032
--- /dev/null
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureGlobalSearchSession.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appfunctions;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchManager;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.GlobalSearchSession;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.app.appsearch.observer.ObserverCallback;
+import android.app.appsearch.observer.ObserverSpec;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.concurrent.Executor;
+
+/** A wrapper around {@link GlobalSearchSession} that provides a future-based API. */
+public class FutureGlobalSearchSession implements Closeable {
+ private static final String TAG = FutureGlobalSearchSession.class.getSimpleName();
+ private final Executor mExecutor;
+ private final AndroidFuture<AppSearchResult<GlobalSearchSession>> mSettableSessionFuture;
+
+ public FutureGlobalSearchSession(
+ @NonNull AppSearchManager appSearchManager, @NonNull Executor executor) {
+ this.mExecutor = executor;
+ mSettableSessionFuture = new AndroidFuture<>();
+ appSearchManager.createGlobalSearchSession(mExecutor, mSettableSessionFuture::complete);
+ }
+
+ private AndroidFuture<GlobalSearchSession> getSessionAsync() {
+ return mSettableSessionFuture.thenApply(
+ result -> {
+ if (result.isSuccess()) {
+ return result.getResultValue();
+ } else {
+ throw new RuntimeException(
+ FutureAppSearchSession.failedResultToException(result));
+ }
+ });
+ }
+
+ /**
+ * Registers an observer callback for the given target package name.
+ *
+ * @param targetPackageName The package name of the target app.
+ * @param spec The observer spec.
+ * @param executor The executor to run the observer callback on.
+ * @param observer The observer callback to register.
+ * @return A future that completes once the observer is registered.
+ */
+ public AndroidFuture<Void> registerObserverCallbackAsync(
+ String targetPackageName,
+ ObserverSpec spec,
+ Executor executor,
+ ObserverCallback observer) {
+ return getSessionAsync()
+ .thenCompose(
+ session -> {
+ try {
+ session.registerObserverCallback(
+ targetPackageName, spec, executor, observer);
+ return AndroidFuture.completedFuture(null);
+ } catch (AppSearchException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ getSessionAsync().get().close();
+ } catch (Exception ex) {
+ Slog.e(TAG, "Failed to close global search session", ex);
+ }
+ }
+}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java
index 98903ae57a39..58597c38bb94 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCaller.java
@@ -25,7 +25,6 @@ import android.os.UserHandle;
* services are properly unbound after the operation completes or a timeout occurs.
*
* @param <T> Class of wrapped service.
- * @hide
*/
public interface RemoteServiceCaller<T> {
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
index 0e18705c40b0..eea17eeca371 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/RemoteServiceCallerImpl.java
@@ -34,7 +34,6 @@ import java.util.function.Function;
* Context#bindService}.
*
* @param <T> Class of wrapped service.
- * @hide
*/
public class RemoteServiceCallerImpl<T> implements RemoteServiceCaller<T> {
private static final String TAG = "AppFunctionsServiceCall";
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt
index 5233f194d6c5..a323799e85e5 100644
--- a/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt
@@ -42,7 +42,7 @@ class FutureAppSearchSessionTest {
fun clearData() {
val searchContext = AppSearchManager.SearchContext.Builder(TEST_DB).build()
FutureAppSearchSession(appSearchManager, testExecutor, searchContext).use {
- val setSchemaRequest = SetSchemaRequest.Builder().build()
+ val setSchemaRequest = SetSchemaRequest.Builder().setForceOverride(true).build()
it.setSchema(setSchemaRequest)
}
}
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/FutureGlobalSearchSessionTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/FutureGlobalSearchSessionTest.kt
new file mode 100644
index 000000000000..8817a66c734e
--- /dev/null
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/FutureGlobalSearchSessionTest.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2023 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.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.appfunctions
+
+import android.app.appfunctions.AppFunctionRuntimeMetadata
+import android.app.appfunctions.AppFunctionRuntimeMetadata.createAppFunctionRuntimeSchema
+import android.app.appfunctions.AppFunctionRuntimeMetadata.createParentAppFunctionRuntimeSchema
+import android.app.appfunctions.AppFunctionStaticMetadataHelper
+import android.app.appsearch.AppSearchManager
+import android.app.appsearch.AppSearchManager.SearchContext
+import android.app.appsearch.PutDocumentsRequest
+import android.app.appsearch.SetSchemaRequest
+import android.app.appsearch.observer.DocumentChangeInfo
+import android.app.appsearch.observer.ObserverCallback
+import android.app.appsearch.observer.ObserverSpec
+import android.app.appsearch.observer.SchemaChangeInfo
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.internal.infra.AndroidFuture
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class FutureGlobalSearchSessionTest {
+ private val context = InstrumentationRegistry.getInstrumentation().targetContext
+ private val appSearchManager = context.getSystemService(AppSearchManager::class.java)
+ private val testExecutor = MoreExecutors.directExecutor()
+
+ @Before
+ @After
+ fun clearData() {
+ val searchContext = SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSession(appSearchManager, testExecutor, searchContext).use {
+ val setSchemaRequest = SetSchemaRequest.Builder().setForceOverride(true).build()
+ it.setSchema(setSchemaRequest)
+ }
+ }
+
+ @Test
+ fun registerDocumentChangeObserverCallback() {
+ val baseObserverSpec: ObserverSpec =
+ ObserverSpec.Builder()
+ .addFilterSchemas(AppFunctionStaticMetadataHelper.STATIC_SCHEMA_TYPE)
+ .build()
+ val packageObserverSpec: ObserverSpec =
+ ObserverSpec.Builder()
+ .addFilterSchemas(
+ AppFunctionRuntimeMetadata.getRuntimeSchemaNameForPackage(TEST_TARGET_PKG_NAME)
+ )
+ .build()
+ val settableDocumentChangeInfo: AndroidFuture<DocumentChangeInfo> = AndroidFuture()
+ val observer: ObserverCallback =
+ object : ObserverCallback {
+ override fun onSchemaChanged(changeInfo: SchemaChangeInfo) {}
+
+ override fun onDocumentChanged(changeInfo: DocumentChangeInfo) {
+ settableDocumentChangeInfo.complete(changeInfo)
+ }
+ }
+ val futureGlobalSearchSession = FutureGlobalSearchSession(appSearchManager, testExecutor)
+
+ val registerBaseObserver: Void? =
+ futureGlobalSearchSession
+ .registerObserverCallbackAsync(
+ TEST_TARGET_PKG_NAME,
+ baseObserverSpec,
+ testExecutor,
+ observer,
+ )
+ .get()
+ val registerPackageObserver: Void? =
+ futureGlobalSearchSession
+ .registerObserverCallbackAsync(
+ TEST_TARGET_PKG_NAME,
+ packageObserverSpec,
+ testExecutor,
+ observer,
+ )
+ .get()
+
+ assertThat(registerBaseObserver).isNull()
+ assertThat(registerPackageObserver).isNull()
+ // Trigger document change
+ val searchContext = SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSession(appSearchManager, testExecutor, searchContext).use { session ->
+ val setSchemaRequest =
+ SetSchemaRequest.Builder()
+ .addSchemas(
+ createParentAppFunctionRuntimeSchema(),
+ createAppFunctionRuntimeSchema(TEST_TARGET_PKG_NAME),
+ )
+ .build()
+ val schema = session.setSchema(setSchemaRequest)
+ assertThat(schema.get()).isNotNull()
+ val appFunctionRuntimeMetadata =
+ AppFunctionRuntimeMetadata.Builder(TEST_TARGET_PKG_NAME, TEST_FUNCTION_ID, "")
+ .build()
+ val putDocumentsRequest: PutDocumentsRequest =
+ PutDocumentsRequest.Builder()
+ .addGenericDocuments(appFunctionRuntimeMetadata)
+ .build()
+ val putResult = session.put(putDocumentsRequest).get()
+ assertThat(putResult.isSuccess).isTrue()
+ }
+ assertThat(
+ settableDocumentChangeInfo
+ .get()
+ .changedDocumentIds
+ .contains(
+ AppFunctionRuntimeMetadata.getDocumentIdForAppFunction(
+ TEST_TARGET_PKG_NAME,
+ TEST_FUNCTION_ID,
+ )
+ )
+ )
+ .isTrue()
+ }
+
+ private companion object {
+ const val TEST_DB: String = "test_db"
+ const val TEST_TARGET_PKG_NAME = "com.android.frameworks.appfunctionstests"
+ const val TEST_FUNCTION_ID: String = "print"
+ }
+}