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

Commit 3311711b authored by Alexander Dorokhine's avatar Alexander Dorokhine
Browse files

Sync AppSearch from framework.

Contains changes:
* 91ce28: Properly check permissions for globalQuery.
* 5663cb: Add cts tests for projection wildcard.
* 0c3732: Add cts test for RELEVANCE_SCORE.
* 79beac: Implement the usage reporting API.
* 7d3c33:  Add Version number to AppSearchSchema

Bug: 169883602
Bug: 175039682
Bug: 174698212
Bug: 177278213
Bug: 177266929
Test: Presubmit
Change-Id: Ia9b59e9957e6022d9b6f4ef82b9d0198952afa30
parent a227fb58
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -40,12 +40,14 @@ package android.app.appsearch {
  public final class AppSearchSchema {
    method @NonNull public java.util.List<android.app.appsearch.AppSearchSchema.PropertyConfig> getProperties();
    method @NonNull public String getSchemaType();
    method @IntRange(from=0) public int getVersion();
  }

  public static final class AppSearchSchema.Builder {
    ctor public AppSearchSchema.Builder(@NonNull String);
    method @NonNull public android.app.appsearch.AppSearchSchema.Builder addProperty(@NonNull android.app.appsearch.AppSearchSchema.PropertyConfig);
    method @NonNull public android.app.appsearch.AppSearchSchema build();
    method @NonNull public android.app.appsearch.AppSearchSchema.Builder setVersion(@IntRange(from=0) int);
  }

  public static final class AppSearchSchema.PropertyConfig {
@@ -89,6 +91,7 @@ package android.app.appsearch {
    method @NonNull public android.app.appsearch.SearchResults query(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor);
    method public void removeByQuery(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
    method public void removeByUri(@NonNull android.app.appsearch.RemoveByUriRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
    method @NonNull public void reportUsage(@NonNull android.app.appsearch.ReportUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
    method public void setSchema(@NonNull android.app.appsearch.SetSchemaRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
  }

@@ -186,7 +189,22 @@ package android.app.appsearch {
    method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder setNamespace(@NonNull String);
  }

  public final class ReportUsageRequest {
    method @NonNull public String getNamespace();
    method @NonNull public String getUri();
    method public long getUsageTimeMillis();
  }

  public static final class ReportUsageRequest.Builder {
    ctor public ReportUsageRequest.Builder();
    method @NonNull public android.app.appsearch.ReportUsageRequest build();
    method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setNamespace(@NonNull String);
    method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUri(@NonNull String);
    method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUsageTimeMillis(long);
  }

  public final class SearchResult {
    method @NonNull public String getDatabaseName();
    method @NonNull public android.app.appsearch.GenericDocument getDocument();
    method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches();
    method @NonNull public String getPackageName();
@@ -230,6 +248,8 @@ package android.app.appsearch {
    field public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1; // 0x1
    field public static final int RANKING_STRATEGY_NONE = 0; // 0x0
    field public static final int RANKING_STRATEGY_RELEVANCE_SCORE = 3; // 0x3
    field public static final int RANKING_STRATEGY_USAGE_COUNT = 4; // 0x4
    field public static final int RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP = 5; // 0x5
    field public static final int TERM_MATCH_EXACT_ONLY = 1; // 0x1
    field public static final int TERM_MATCH_PREFIX = 2; // 0x2
  }
+45 −0
Original line number Diff line number Diff line
@@ -419,6 +419,51 @@ public final class AppSearchSession implements Closeable {
                searchSpec, mUserId, executor);
    }

    /**
     * Reports usage of a particular document by URI and namespace.
     *
     * <p>A usage report represents an event in which a user interacted with or viewed a document.
     *
     * <p>For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency
     * metrics for that particular document. These metrics are used for ordering {@link #query}
     * results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and
     * {@link SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies.
     *
     * <p>Reporting usage of a document is optional.
     *
     * @param request The usage reporting request.
     * @param executor Executor on which to invoke the callback.
     * @param callback Callback to receive errors. If the operation succeeds, the callback will be
     *                 invoked with {@code null}.
     */
    @NonNull
    public void reportUsage(
            @NonNull ReportUsageRequest request,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull Consumer<AppSearchResult<Void>> callback) {
        Objects.requireNonNull(request);
        Objects.requireNonNull(executor);
        Objects.requireNonNull(callback);
        Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
        try {
            mService.reportUsage(
                    mPackageName,
                    mDatabaseName,
                    request.getNamespace(),
                    request.getUri(),
                    request.getUsageTimeMillis(),
                    mUserId,
                    new IAppSearchResultCallback.Stub() {
                        public void onResult(AppSearchResult result) {
                            executor.execute(() -> callback.accept(result));
                        }
                    });
            mIsMutated = true;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Removes {@link GenericDocument}s from the index by URI.
     *
+30 −0
Original line number Diff line number Diff line
@@ -170,6 +170,36 @@ interface IAppSearchManager {
     */
    void invalidateNextPageToken(in long nextPageToken, in int userId);

    /**
     * Reports usage of a particular document by URI and namespace.
     *
     * <p>A usage report represents an event in which a user interacted with or viewed a document.
     *
     * <p>For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency
     * metrics for that particular document. These metrics are used for ordering {@link #query}
     * results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and
     * {@link SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies.
     *
     * <p>Reporting usage of a document is optional.
     *
     * @param packageName The name of the package that owns this document.
     * @param databaseName  The name of the database to report usage against.
     * @param namespace Namespace the document being used belongs to.
     * @param uri URI of the document being used.
     * @param usageTimeMillis The timestamp at which the document was used.
     * @param userId Id of the calling user
     * @param callback {@link IAppSearchResultCallback#onResult} will be called with an
     *     {@link AppSearchResult}&lt;{@link Void}&gt;.
     */
     void reportUsage(
         in String packageName,
         in String databaseName,
         in String namespace,
         in String uri,
         in long usageTimeMillis,
         in int userId,
         in IAppSearchResultCallback callback);

    /**
     * Removes documents by URI.
     *
+48 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.app.appsearch;

import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -46,6 +47,7 @@ import java.util.Set;
 */
public final class AppSearchSchema {
    private static final String SCHEMA_TYPE_FIELD = "schemaType";
    private static final String VERSION_FIELD = "version";
    private static final String PROPERTIES_FIELD = "properties";

    private final Bundle mBundle;
@@ -77,6 +79,11 @@ public final class AppSearchSchema {
        return mBundle.getString(SCHEMA_TYPE_FIELD, "");
    }

    /** Returns the version of this {@link AppSearchSchema}. */
    public @IntRange(from = 0) int getVersion() {
        return mBundle.getInt(VERSION_FIELD);
    }

    /**
     * Returns the list of {@link PropertyConfig}s that are part of this schema.
     *
@@ -108,12 +115,15 @@ public final class AppSearchSchema {
        if (!getSchemaType().equals(otherSchema.getSchemaType())) {
            return false;
        }
        if (getVersion() != otherSchema.getVersion()) {
            return false;
        }
        return getProperties().equals(otherSchema.getProperties());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getSchemaType(), getProperties());
        return Objects.hash(getSchemaType(), getVersion(), getProperties());
    }

    /** Builder for {@link AppSearchSchema objects}. */
@@ -121,6 +131,7 @@ public final class AppSearchSchema {
        private final String mSchemaType;
        private final ArrayList<Bundle> mPropertyBundles = new ArrayList<>();
        private final Set<String> mPropertyNames = new ArraySet<>();
        private int mVersion;
        private boolean mBuilt = false;

        /** Creates a new {@link AppSearchSchema.Builder}. */
@@ -146,6 +157,41 @@ public final class AppSearchSchema {
            return this;
        }

        /**
         * Sets the version number of the {@link AppSearchSchema}.
         *
         * <p>The {@link AppSearchSession} database can only ever hold documents for one version of
         * a {@link AppSearchSchema} type at a time.
         *
         * <p>Setting a version number that is different from the version number of the schema
         * currently stored in AppSearch will result in AppSearch calling the Migrator provided to
         * {@link AppSearchSession#setSchema} to migrate the documents already in AppSearch from the
         * previous version to the one set in this request. The version number can be updated
         * without any other changes to the schema.
         *
         * <p>The version number can stay the same, increase, or decrease relative to the current
         * version number of the {@link AppSearchSchema} type that is already stored in the {@link
         * AppSearchSession} database.
         *
         * <p>The version number will be updated if the {@link SetSchemaRequest} contains
         * backwards-compatible changes or {@link SetSchemaRequest.Builder#setForceOverride} method
         * is set to {@code true}.
         *
         * @param version A non-negative int number represents the version of this {@link
         *     AppSearchSchema}, default version is 0.
         * @throws IllegalStateException if the version is negative or the builder has already been
         *     used.
         * @see AppSearchSession#setSchema
         */
        // TODO(b/177266929) link to Migrator in once it's ready.
        @NonNull
        public AppSearchSchema.Builder setVersion(@IntRange(from = 0) int version) {
            Preconditions.checkState(!mBuilt, "Builder has already been used");
            Preconditions.checkArgumentNonnegative(version);
            mVersion = version;
            return this;
        }

        /**
         * Constructs a new {@link AppSearchSchema} from the contents of this builder.
         *
@@ -156,6 +202,7 @@ public final class AppSearchSchema {
            Preconditions.checkState(!mBuilt, "Builder has already been used");
            Bundle bundle = new Bundle();
            bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mSchemaType);
            bundle.putInt(AppSearchSchema.VERSION_FIELD, mVersion);
            bundle.putParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD, mPropertyBundles);
            mBuilt = true;
            return new AppSearchSchema(bundle);
+135 −0
Original line number Diff line number Diff line
/*
 * Copyright 2021 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.NonNull;

import com.android.internal.util.Preconditions;

/**
 * A request to report usage of a document.
 *
 * <p>See {@link AppSearchSession#reportUsage} for a detailed description of usage reporting.
 *
 * @see AppSearchSession#reportUsage
 */
public final class ReportUsageRequest {
    private final String mNamespace;
    private final String mUri;
    private final long mUsageTimeMillis;

    ReportUsageRequest(@NonNull String namespace, @NonNull String uri, long usageTimeMillis) {
        mNamespace = Preconditions.checkNotNull(namespace);
        mUri = Preconditions.checkNotNull(uri);
        mUsageTimeMillis = usageTimeMillis;
    }

    /** Returns the namespace of the document that was used. */
    @NonNull
    public String getNamespace() {
        return mNamespace;
    }

    /** Returns the URI of document that was used. */
    @NonNull
    public String getUri() {
        return mUri;
    }

    /**
     * Returns the timestamp in milliseconds of the usage report (the time at which the document was
     * used).
     *
     * <p>The value is in the {@link System#currentTimeMillis} time base.
     */
    public long getUsageTimeMillis() {
        return mUsageTimeMillis;
    }

    /** Builder for {@link ReportUsageRequest} objects. */
    public static final class Builder {
        private String mNamespace = GenericDocument.DEFAULT_NAMESPACE;
        private String mUri;
        private Long mUsageTimeMillis;
        private boolean mBuilt = false;

        /**
         * Sets which namespace the document being used belongs to.
         *
         * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}.
         *
         * @throws IllegalStateException if the builder has already been used
         */
        @NonNull
        public ReportUsageRequest.Builder setNamespace(@NonNull String namespace) {
            Preconditions.checkState(!mBuilt, "Builder has already been used");
            Preconditions.checkNotNull(namespace);
            mNamespace = namespace;
            return this;
        }

        /**
         * Sets the URI of the document being used.
         *
         * <p>This field is required.
         *
         * @throws IllegalStateException if the builder has already been used
         */
        @NonNull
        public ReportUsageRequest.Builder setUri(@NonNull String uri) {
            Preconditions.checkState(!mBuilt, "Builder has already been used");
            Preconditions.checkNotNull(uri);
            mUri = uri;
            return this;
        }

        /**
         * Sets the timestamp in milliseconds of the usage report (the time at which the document
         * was used).
         *
         * <p>The value is in the {@link System#currentTimeMillis} time base.
         *
         * <p>If unset, this defaults to the current timestamp at the time that the {@link
         * ReportUsageRequest} is constructed.
         *
         * @throws IllegalStateException if the builder has already been used
         */
        @NonNull
        public ReportUsageRequest.Builder setUsageTimeMillis(long usageTimeMillis) {
            Preconditions.checkState(!mBuilt, "Builder has already been used");
            mUsageTimeMillis = usageTimeMillis;
            return this;
        }

        /**
         * Builds a new {@link ReportUsageRequest}.
         *
         * @throws NullPointerException if {@link #setUri} has never been called
         * @throws IllegalStateException if the builder has already been used
         */
        @NonNull
        public ReportUsageRequest build() {
            Preconditions.checkState(!mBuilt, "Builder has already been used");
            Preconditions.checkNotNull(mUri, "ReportUsageRequest is missing a URI");
            if (mUsageTimeMillis == null) {
                mUsageTimeMillis = System.currentTimeMillis();
            }
            mBuilt = true;
            return new ReportUsageRequest(mNamespace, mUri, mUsageTimeMillis);
        }
    }
}
Loading