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

Commit b00edb69 authored by Terry Wang's avatar Terry Wang Committed by Android (Google) Code Review
Browse files

Merge "Add GlobalSearchSession to AppSearch platform."

parents bf7f6352 516b009a
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
@@ -140,6 +140,25 @@ public class AppSearchManager {
        AppSearchSession.createSearchSession(searchContext, mService, executor, callback);
    }

    /**
     * Creates a new {@link GlobalSearchSession}.
     *
     * <p>This process requires an AppSearch native indexing file system for each user. If it's not
     * created for this user, the initialization process will create one under user's directory.
     *
     * @param executor      Executor on which to invoke the callback.
     * @param callback      The {@link AppSearchResult}&lt;{@link GlobalSearchSession}&gt; of
     *                      performing this operation. Or a {@link AppSearchResult} with failure
     *                      reason code and error information.
     */
    public void createGlobalSearchSession(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) {
        Objects.requireNonNull(executor);
        Objects.requireNonNull(callback);
        GlobalSearchSession.createGlobalSearchSession(mService, executor, callback);
    }

    /**
     * Sets the schema being used by documents provided to the {@link #putDocuments} method.
     *
+128 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 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.
 * 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 android.app.appsearch;


import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.os.RemoteException;

import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * This class provides global access to the centralized AppSearch index maintained by the system.
 *
 * <p>Apps can retrieve indexed documents through the query API.
 * @hide
 */
public class GlobalSearchSession {

    private final IAppSearchManager mService;

    static void createGlobalSearchSession(
            @NonNull IAppSearchManager service,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) {
        GlobalSearchSession globalSearchSession = new GlobalSearchSession(service);
        globalSearchSession.initialize(executor, callback);
    }

    // NOTE: No instance of this class should be created or returned except via initialize().
    // Once the callback.accept has been called here, the class is ready to use.
    private void initialize(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull Consumer<AppSearchResult<GlobalSearchSession>> callback) {
        try {
            mService.initialize(new IAppSearchResultCallback.Stub() {
                public void onResult(AppSearchResult result) {
                    executor.execute(() -> {
                        if (result.isSuccess()) {
                            callback.accept(
                                    AppSearchResult.newSuccessfulResult(GlobalSearchSession.this));
                        } else {
                            callback.accept(result);
                        }
                    });
                }
            });
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    private GlobalSearchSession(@NonNull IAppSearchManager service) {
        mService = service;
    }

    /**
     * Searches across all documents in the storage based on a given query string.
     *
     * <p>Currently we support following features in the raw query format:
     * <ul>
     *     <li>AND
     *     <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and
     *     ‘cat’”).
     *     Example: hello world matches documents that have both ‘hello’ and ‘world’
     *     <li>OR
     *     <p>OR joins (e.g. “match documents that have either the term ‘dog’ or
     *     ‘cat’”).
     *     Example: dog OR puppy
     *     <li>Exclusion
     *     <p>Exclude a term (e.g. “match documents that do
     *     not have the term ‘dog’”).
     *     Example: -dog excludes the term ‘dog’
     *     <li>Grouping terms
     *     <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g.
     *     “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”).
     *     Example: (dog puppy) (cat kitten) two one group containing two terms.
     *     <li>Property restricts
     *     <p> Specifies which properties of a document to specifically match terms in (e.g.
     *     “match documents where the ‘subject’ property contains ‘important’”).
     *     Example: subject:important matches documents with the term ‘important’ in the
     *     ‘subject’ property
     *     <li>Schema type restricts
     *     <p>This is similar to property restricts, but allows for restricts on top-level document
     *     fields, such as schema_type. Clients should be able to limit their query to documents of
     *     a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”).
     *     Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents
     *     that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the
     *     ‘Video’ schema type.
     * </ul>
     *
     * <p> This method is lightweight. The heavy work will be done in
     * {@link SearchResults#getNextPage}.
     *
     * @param queryExpression Query String to search.
     * @param searchSpec      Spec for setting filters, raw query etc.
     * @param executor        Executor on which to invoke the callback of the following request
     *                        {@link SearchResults#getNextPage}.
     * @return The search result of performing this operation.
     */
    @NonNull
    public SearchResults globalQuery(
            @NonNull String queryExpression,
            @NonNull SearchSpec searchSpec,
            @NonNull @CallbackExecutor Executor executor) {
        Objects.requireNonNull(queryExpression);
        Objects.requireNonNull(searchSpec);
        Objects.requireNonNull(executor);
        return new SearchResults(mService, /*databaseName=*/null, queryExpression,
                searchSpec, executor);
    }
}
+14 −0
Original line number Diff line number Diff line
@@ -94,6 +94,20 @@ interface IAppSearchManager {
        in Bundle searchSpecBundle,
        in IAppSearchResultCallback callback);

    /**
     * Executes a global query, i.e. over all permitted databases, against the AppSearch index and
     * returns results.
     *
     * @param queryExpression String to search for
     * @param searchSpecBundle SearchSpec bundle
     * @param callback {@link AppSearchResult}&lt;{@link Bundle}&gt; of performing this
     *         operation.
     */
    void globalQuery(
        in String queryExpression,
        in Bundle searchSpecBundle,
        in IAppSearchResultCallback callback);

    /**
     * Fetches the next page of results of a previously executed query. Results can be empty if
     * next-page token is invalid or all pages have been returned.
+18 −14
Original line number Diff line number Diff line
@@ -85,23 +85,18 @@ public class SearchResults implements Closeable {
        try {
            if (mIsFirstLoad) {
                mIsFirstLoad = false;
                //TODO(b/162450968) add support for global query.
                if (mDatabaseName == null) {
                    mService.globalQuery(mQueryExpression, mSearchSpec.getBundle(),
                            wrapCallback(callback));
                } else {
                    mService.query(mDatabaseName, mQueryExpression, mSearchSpec.getBundle(),
                        new IAppSearchResultCallback.Stub() {
                            public void onResult(AppSearchResult result) {
                                mExecutor.execute(() -> invokeCallback(result, callback));
                            wrapCallback(callback));
                }
                        });
            } else {
                mService.getNextPage(mNextPageToken,
                        new IAppSearchResultCallback.Stub() {
                            public void onResult(AppSearchResult result) {
                                mExecutor.execute(() -> invokeCallback(result, callback));
                            }
                        });
                mService.getNextPage(mNextPageToken, wrapCallback(callback));
            }
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
            throw e.rethrowFromSystemServer();
        }
    }

@@ -131,4 +126,13 @@ public class SearchResults implements Closeable {
            }
        });
    }

    private IAppSearchResultCallback wrapCallback(
            @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
        return new IAppSearchResultCallback.Stub() {
            public void onResult(AppSearchResult result) {
                mExecutor.execute(() -> invokeCallback(result, callback));
            }
        };
    }
}
+29 −0
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ public class AppSearchManagerService extends SystemService {
                @NonNull IAppSearchResultCallback callback) {
            Preconditions.checkNotNull(databaseName);
            Preconditions.checkNotNull(schemaBundles);
            Preconditions.checkNotNull(callback);
            int callingUid = Binder.getCallingUidOrThrow();
            int callingUserId = UserHandle.getUserId(callingUid);
            final long callingIdentity = Binder.clearCallingIdentity();
@@ -166,6 +167,7 @@ public class AppSearchManagerService extends SystemService {
            Preconditions.checkNotNull(databaseName);
            Preconditions.checkNotNull(queryExpression);
            Preconditions.checkNotNull(searchSpecBundle);
            Preconditions.checkNotNull(callback);
            int callingUid = Binder.getCallingUidOrThrow();
            int callingUserId = UserHandle.getUserId(callingUid);
            final long callingIdentity = Binder.clearCallingIdentity();
@@ -185,9 +187,34 @@ public class AppSearchManagerService extends SystemService {
            }
        }

        public void globalQuery(
                @NonNull String queryExpression,
                @NonNull Bundle searchSpecBundle,
                @NonNull IAppSearchResultCallback callback) {
            Preconditions.checkNotNull(queryExpression);
            Preconditions.checkNotNull(searchSpecBundle);
            Preconditions.checkNotNull(callback);
            int callingUid = Binder.getCallingUidOrThrow();
            int callingUserId = UserHandle.getUserId(callingUid);
            final long callingIdentity = Binder.clearCallingIdentity();
            try {
                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                SearchResultPage searchResultPage = impl.globalQuery(
                        queryExpression,
                        new SearchSpec(searchSpecBundle));
                invokeCallbackOnResult(callback,
                        AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
            } catch (Throwable t) {
                invokeCallbackOnError(callback, t);
            } finally {
                Binder.restoreCallingIdentity(callingIdentity);
            }
        }

        @Override
        public void getNextPage(long nextPageToken,
                @NonNull IAppSearchResultCallback callback) {
            Preconditions.checkNotNull(callback);
            int callingUid = Binder.getCallingUidOrThrow();
            int callingUserId = UserHandle.getUserId(callingUid);
            final long callingIdentity = Binder.clearCallingIdentity();
@@ -261,6 +288,7 @@ public class AppSearchManagerService extends SystemService {
            Preconditions.checkNotNull(databaseName);
            Preconditions.checkNotNull(queryExpression);
            Preconditions.checkNotNull(searchSpecBundle);
            Preconditions.checkNotNull(callback);
            int callingUid = Binder.getCallingUidOrThrow();
            int callingUserId = UserHandle.getUserId(callingUid);
            final long callingIdentity = Binder.clearCallingIdentity();
@@ -279,6 +307,7 @@ public class AppSearchManagerService extends SystemService {

        @Override
        public void initialize(@NonNull IAppSearchResultCallback callback) {
            Preconditions.checkNotNull(callback);
            int callingUid = Binder.getCallingUidOrThrow();
            int callingUserId = UserHandle.getUserId(callingUid);
            final long callingIdentity = Binder.clearCallingIdentity();