Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bca640f6 authored by Oluwarotimi Adesina's avatar Oluwarotimi Adesina
Browse files

Introduce FutureGlobalSearchSession

Flag: android.app.appfunctions.flags.enable_app_function_manager
Test: atest FrameworksAppFunctionsTests -c
Bug: 357551503

Change-Id: I5517ca7dffea5b8a8e9925de7da9880ae247b8c8
parent 53950fa3
Loading
Loading
Loading
Loading
+30 −36
Original line number Diff line number Diff line
@@ -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,12 +131,14 @@ public class FutureAppSearchSession implements Closeable {
    /** Indexes documents into the AppSearchSession database. */
    public AndroidFuture<AppSearchBatchResult<String, Void>> put(
            @NonNull PutDocumentsRequest putDocumentsRequest) {
        return getSessionAsync().thenCompose(
        return getSessionAsync()
                .thenCompose(
                        session -> {
                            AndroidFuture<AppSearchBatchResult<String, Void>> batchResultFuture =
                                    new AndroidFuture<>();

                    session.put(putDocumentsRequest, mExecutor, batchResultFuture::complete);
                            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 -> {
            return nextPageFuture.thenApply(
                    result -> {
                        if (result.isSuccess()) {
                            return result.getResultValue();
                        } else {
                    throw new RuntimeException(
                            failedResultToException(result));
                            throw new RuntimeException(failedResultToException(result));
                        }
                    });
        }

    }
}
+94 −0
Original line number Diff line number Diff line
/*
 * 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);
        }
    }
}
+0 −1
Original line number Diff line number Diff line
@@ -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> {

+0 −1
Original line number Diff line number Diff line
@@ -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";
+1 −1
Original line number Diff line number Diff line
@@ -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)
        }
    }
Loading