diff options
42 files changed, 1343 insertions, 1015 deletions
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index 5acfe6ab6430..7441b0f1e22b 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -79,15 +79,14 @@ package android.app.appsearch { public static final class AppSearchSchema.DocumentPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig { method @NonNull public String getSchemaType(); - method public boolean isIndexNestedProperties(); + method public boolean shouldIndexNestedProperties(); } public static final class AppSearchSchema.DocumentPropertyConfig.Builder { - ctor public AppSearchSchema.DocumentPropertyConfig.Builder(@NonNull String); + ctor public AppSearchSchema.DocumentPropertyConfig.Builder(@NonNull String, @NonNull String); method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig build(); method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setCardinality(int); - method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setIndexNestedProperties(boolean); - method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setSchemaType(@NonNull String); + method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setShouldIndexNestedProperties(boolean); } public static final class AppSearchSchema.DoublePropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig { @@ -136,12 +135,12 @@ package android.app.appsearch { public final class AppSearchSession implements java.io.Closeable { method public void close(); - method public void getByUri(@NonNull android.app.appsearch.GetByUriRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,android.app.appsearch.GenericDocument>); + method public void getByDocumentId(@NonNull android.app.appsearch.GetByDocumentIdRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,android.app.appsearch.GenericDocument>); method public void getNamespaces(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.util.Set<java.lang.String>>>); method public void getSchema(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.GetSchemaResponse>>); method public void getStorageInfo(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.StorageInfo>>); method public void put(@NonNull android.app.appsearch.PutDocumentsRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>); - method public void remove(@NonNull android.app.appsearch.RemoveByUriRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>); + method public void remove(@NonNull android.app.appsearch.RemoveByDocumentIdRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>); method public void remove(@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 reportUsage(@NonNull android.app.appsearch.ReportUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>); method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec); @@ -156,6 +155,7 @@ package android.app.appsearch { public class GenericDocument { ctor protected GenericDocument(@NonNull android.app.appsearch.GenericDocument); method public long getCreationTimestampMillis(); + method @NonNull public String getId(); method public static int getMaxIndexedProperties(); method @NonNull public String getNamespace(); method @Nullable public Object getProperty(@NonNull String); @@ -175,7 +175,6 @@ package android.app.appsearch { method @NonNull public String getSchemaType(); method public int getScore(); method public long getTtlMillis(); - method @NonNull public String getUri(); } public static class GenericDocument.Builder<BuilderType extends android.app.appsearch.GenericDocument.Builder> { @@ -192,19 +191,19 @@ package android.app.appsearch { method @NonNull public BuilderType setTtlMillis(long); } - public final class GetByUriRequest { + public final class GetByDocumentIdRequest { + method @NonNull public java.util.Set<java.lang.String> getIds(); method @NonNull public String getNamespace(); method @NonNull public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getProjections(); - method @NonNull public java.util.Set<java.lang.String> getUris(); field public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*"; } - public static final class GetByUriRequest.Builder { - ctor public GetByUriRequest.Builder(@NonNull String); - method @NonNull public android.app.appsearch.GetByUriRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>); - method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.lang.String...); - method @NonNull public android.app.appsearch.GetByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>); - method @NonNull public android.app.appsearch.GetByUriRequest build(); + public static final class GetByDocumentIdRequest.Builder { + ctor public GetByDocumentIdRequest.Builder(@NonNull String); + method @NonNull public android.app.appsearch.GetByDocumentIdRequest.Builder addIds(@NonNull java.lang.String...); + method @NonNull public android.app.appsearch.GetByDocumentIdRequest.Builder addIds(@NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.GetByDocumentIdRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.GetByDocumentIdRequest build(); } public class GetSchemaResponse { @@ -249,44 +248,42 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.PutDocumentsRequest build(); } - public final class RemoveByUriRequest { + public final class RemoveByDocumentIdRequest { + method @NonNull public java.util.Set<java.lang.String> getIds(); method @NonNull public String getNamespace(); - method @NonNull public java.util.Set<java.lang.String> getUris(); } - public static final class RemoveByUriRequest.Builder { - ctor public RemoveByUriRequest.Builder(@NonNull String); - method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.lang.String...); - method @NonNull public android.app.appsearch.RemoveByUriRequest.Builder addUris(@NonNull java.util.Collection<java.lang.String>); - method @NonNull public android.app.appsearch.RemoveByUriRequest build(); + public static final class RemoveByDocumentIdRequest.Builder { + ctor public RemoveByDocumentIdRequest.Builder(@NonNull String); + method @NonNull public android.app.appsearch.RemoveByDocumentIdRequest.Builder addIds(@NonNull java.lang.String...); + method @NonNull public android.app.appsearch.RemoveByDocumentIdRequest.Builder addIds(@NonNull java.util.Collection<java.lang.String>); + method @NonNull public android.app.appsearch.RemoveByDocumentIdRequest build(); } public final class ReportSystemUsageRequest { method @NonNull public String getDatabaseName(); + method @NonNull public String getDocumentId(); method @NonNull public String getNamespace(); method @NonNull public String getPackageName(); - method @NonNull public String getUri(); - method public long getUsageTimeMillis(); + method public long getUsageTimestampMillis(); } public static final class ReportSystemUsageRequest.Builder { - ctor public ReportSystemUsageRequest.Builder(@NonNull String, @NonNull String, @NonNull String); + ctor public ReportSystemUsageRequest.Builder(@NonNull String, @NonNull String, @NonNull String, @NonNull String); method @NonNull public android.app.appsearch.ReportSystemUsageRequest build(); - method @NonNull public android.app.appsearch.ReportSystemUsageRequest.Builder setUri(@NonNull String); - method @NonNull public android.app.appsearch.ReportSystemUsageRequest.Builder setUsageTimeMillis(long); + method @NonNull public android.app.appsearch.ReportSystemUsageRequest.Builder setUsageTimestampMillis(long); } public final class ReportUsageRequest { + method @NonNull public String getDocumentId(); method @NonNull public String getNamespace(); - method @NonNull public String getUri(); - method public long getUsageTimeMillis(); + method public long getUsageTimestampMillis(); } public static final class ReportUsageRequest.Builder { - ctor public ReportUsageRequest.Builder(@NonNull String); + ctor public ReportUsageRequest.Builder(@NonNull String, @NonNull String); method @NonNull public android.app.appsearch.ReportUsageRequest build(); - method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUri(@NonNull String); - method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUsageTimeMillis(long); + method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUsageTimestampMillis(long); } public final class SearchResult { @@ -315,10 +312,9 @@ package android.app.appsearch { } public static final class SearchResult.MatchInfo.Builder { - ctor public SearchResult.MatchInfo.Builder(); + ctor public SearchResult.MatchInfo.Builder(@NonNull String); method @NonNull public android.app.appsearch.SearchResult.MatchInfo build(); method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setExactMatchRange(@NonNull android.app.appsearch.SearchResult.MatchRange); - method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setPropertyPath(@NonNull String); method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setSnippetRange(@NonNull android.app.appsearch.SearchResult.MatchRange); } @@ -427,19 +423,11 @@ package android.app.appsearch { } public static class SetSchemaResponse.MigrationFailure { + ctor public SetSchemaResponse.MigrationFailure(@NonNull String, @NonNull String, @NonNull String, @NonNull android.app.appsearch.AppSearchResult<?>); method @NonNull public android.app.appsearch.AppSearchResult<java.lang.Void> getAppSearchResult(); + method @NonNull public String getDocumentId(); method @NonNull public String getNamespace(); method @NonNull public String getSchemaType(); - method @NonNull public String getUri(); - } - - public static final class SetSchemaResponse.MigrationFailure.Builder { - ctor public SetSchemaResponse.MigrationFailure.Builder(); - method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure build(); - method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setAppSearchResult(@NonNull android.app.appsearch.AppSearchResult<java.lang.Void>); - method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setNamespace(@NonNull String); - method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setSchemaType(@NonNull String); - method @NonNull public android.app.appsearch.SetSchemaResponse.MigrationFailure.Builder setUri(@NonNull String); } public class StorageInfo { diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java index 6c624264b9be..9ae0d622dfae 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java @@ -40,7 +40,7 @@ import java.util.Objects; * both successes and failures. * * @see AppSearchSession#put - * @see AppSearchSession#getByUri + * @see AppSearchSession#getByDocumentId * @see AppSearchSession#remove */ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { @@ -87,8 +87,9 @@ public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelabl * Returns a {@link Map} of keys mapped to instances of the value type for all successful * individual results. * - * <p>Example: {@link AppSearchSession#getByUri} returns an {@link AppSearchBatchResult}. Each - * key (a URI of {@code String} type) will map to a {@link GenericDocument} object. + * <p>Example: {@link AppSearchSession#getByDocumentId} returns an {@link AppSearchBatchResult}. + * Each key (the document ID, of {@code String} type) will map to a {@link GenericDocument} + * object. * * <p>The values of the {@link Map} will not be {@code null}. */ diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index 8af91b480889..c4dd1a0bc4e5 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -78,10 +78,10 @@ import java.util.function.Consumer; * });</pre> * * <p>The basic unit of data in AppSearch is represented as a {@link GenericDocument} object, - * containing a URI, namespace, time-to-live, score, and properties. A namespace organizes a logical + * containing an ID, namespace, time-to-live, score, and properties. A namespace organizes a logical * group of documents. For example, a namespace can be created to group documents on a per-account - * basis. A URI identifies a single document within a namespace. The combination of URI and - * namespace uniquely identifies a {@link GenericDocument} in the database. + * basis. An ID identifies a single document within a namespace. The combination of namespace and ID + * uniquely identifies a {@link GenericDocument} in the database. * * <p>Once the schema has been set, {@link GenericDocument} objects can be put into the database and * indexed by calling {@link AppSearchSession#put}. @@ -91,8 +91,7 @@ import java.util.function.Consumer; * <pre> * // Although for this example we use GenericDocument directly, we recommend extending * // GenericDocument to create specific types (i.e. Email) with specific setters/getters. - * GenericDocument email = new GenericDocument.Builder<>(URI, EMAIL_SCHEMA_TYPE) - * .setNamespace(NAMESPACE) + * GenericDocument email = new GenericDocument.Builder<>(NAMESPACE, ID, EMAIL_SCHEMA_TYPE) * .setPropertyString(“subject”, EMAIL_SUBJECT) * .setScore(EMAIL_SCORE) * .build(); @@ -108,13 +107,13 @@ import java.util.function.Consumer; * <p>Searching within the database is done by calling {@link AppSearchSession#search} and providing * the query string to search for, as well as a {@link SearchSpec}. * - * <p>Alternatively, {@link AppSearchSession#getByUri} can be called to retrieve documents by URI - * and namespace. + * <p>Alternatively, {@link AppSearchSession#getByDocumentId} can be called to retrieve documents by + * namespace and ID. * * <p>Document removal is done either by time-to-live expiration, or explicitly calling a remove - * operation. Remove operations can be done by URI and namespace via {@link - * AppSearchSession#remove(RemoveByUriRequest, Executor, BatchResultCallback)}, or by query via - * {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}. + * operation. Remove operations can be done by namespace and ID via {@link + * AppSearchSession#remove(RemoveByDocumentIdRequest, Executor, BatchResultCallback)}, or by query + * via {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}. */ @SystemService(Context.APP_SEARCH_SERVICE) public class AppSearchManager { diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index c85c4c3a8a7f..1e0d205230ca 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java @@ -41,10 +41,14 @@ import java.util.concurrent.Executor; import java.util.function.Consumer; /** - * Represents a connection to an AppSearch storage system where {@link GenericDocument}s can be - * placed and queried. + * Provides a connection to a single AppSearch database. + * + * <p>An {@link AppSearchSession} instance provides access to database operations such as + * setting a schema, adding documents, and searching. * * <p>This class is thread safe. + * + * @see GlobalSearchSession */ public final class AppSearchSession implements Closeable { private static final String TAG = "AppSearchSession"; @@ -250,12 +254,12 @@ public final class AppSearchSession implements Closeable { * @param request containing documents to be indexed. * @param executor Executor on which to invoke the callback. * @param callback Callback to receive pending result of performing this operation. The keys - * of the returned {@link AppSearchBatchResult} are the URIs of the input + * of the returned {@link AppSearchBatchResult} are the IDs of the input * documents. The values are {@code null} if they were successfully indexed, - * or a failed {@link AppSearchResult} otherwise. - * Or {@link BatchResultCallback#onSystemError} will be invoked with a - * {@link Throwable} if an unexpected internal error occurred in AppSearch - * service. + * or a failed {@link AppSearchResult} otherwise. If an unexpected internal + * error occurs in the AppSearch service, + * {@link BatchResultCallback#onSystemError} will be invoked with a + * {@link Throwable}. */ public void put( @NonNull PutDocumentsRequest request, @@ -291,23 +295,22 @@ public final class AppSearchSession implements Closeable { } /** - * Gets {@link GenericDocument} objects by URIs and namespace from the {@link AppSearchSession} - * database. + * Gets {@link GenericDocument} objects by document IDs in a namespace from the {@link + * AppSearchSession} database. * - * @param request a request containing URIs and namespace to get documents for. + * @param request a request containing a namespace and IDs to get documents for. * @param executor Executor on which to invoke the callback. * @param callback Callback to receive the pending result of performing this operation. The keys - * of the returned {@link AppSearchBatchResult} are the input URIs. The values + * of the returned {@link AppSearchBatchResult} are the input IDs. The values * are the returned {@link GenericDocument}s on success, or a failed - * {@link AppSearchResult} otherwise. URIs that are not found will return a + * {@link AppSearchResult} otherwise. IDs that are not found will return a * failed {@link AppSearchResult} with a result code of - * {@link AppSearchResult#RESULT_NOT_FOUND}. - * Or {@link BatchResultCallback#onSystemError} will be invoked with a - * {@link Throwable} if an unexpected internal error occurred in AppSearch - * service. + * {@link AppSearchResult#RESULT_NOT_FOUND}. If an unexpected internal error + * occurs in the AppSearch service, {@link BatchResultCallback#onSystemError} + * will be invoked with a {@link Throwable}. */ - public void getByUri( - @NonNull GetByUriRequest request, + public void getByDocumentId( + @NonNull GetByDocumentIdRequest request, @NonNull @CallbackExecutor Executor executor, @NonNull BatchResultCallback<String, GenericDocument> callback) { Objects.requireNonNull(request); @@ -319,7 +322,7 @@ public final class AppSearchSession implements Closeable { mPackageName, mDatabaseName, request.getNamespace(), - new ArrayList<>(request.getUris()), + new ArrayList<>(request.getIds()), request.getProjectionsInternal(), mUserId, new IAppSearchBatchResultCallback.Stub() { @@ -373,8 +376,8 @@ public final class AppSearchSession implements Closeable { } /** - * Retrieves documents from the open {@link AppSearchSession} that match a given query string - * and type of search provided. + * Retrieves documents from the open {@link AppSearchSession} that match a given query + * string and type of search provided. * * <p>Query strings can be empty, contain one term with no operators, or contain multiple terms * and operators. @@ -441,7 +444,7 @@ public final class AppSearchSession implements Closeable { } /** - * Reports usage of a particular document by URI and namespace. + * Reports usage of a particular document by namespace and ID. * * <p>A usage report represents an event in which a user interacted with or viewed a document. * @@ -470,8 +473,8 @@ public final class AppSearchSession implements Closeable { mPackageName, mDatabaseName, request.getNamespace(), - request.getUri(), - request.getUsageTimeMillis(), + request.getDocumentId(), + request.getUsageTimestampMillis(), /*systemUsage=*/ false, mUserId, new IAppSearchResultCallback.Stub() { @@ -486,29 +489,29 @@ public final class AppSearchSession implements Closeable { } /** - * Removes {@link GenericDocument} objects by URIs and namespace from the {@link + * Removes {@link GenericDocument} objects by document IDs in a namespace from the {@link * AppSearchSession} database. * - * <p>Removed documents will no longer be surfaced by {@link #search} or {@link #getByUri} - * calls. + * <p>Removed documents will no longer be surfaced by {@link #search} or {@link + * #getByDocumentId} calls. * - * <p><b>NOTE:</b>By default, documents are removed via a soft delete operation. Once the - * document crosses the count threshold or byte usage threshold, the documents will be removed - * from disk. + * <p>Once the database crosses the document count or byte usage threshold, removed documents + * will be deleted from disk. * - * @param request {@link RemoveByUriRequest} with URIs and namespace to remove from the index. + * @param request {@link RemoveByDocumentIdRequest} with IDs in a namespace to remove from the + * index. * @param executor Executor on which to invoke the callback. * @param callback Callback to receive the pending result of performing this operation. The keys - * of the returned {@link AppSearchBatchResult} are the input URIs. The values - * are {@code null} on success, or a failed {@link AppSearchResult} otherwise. - * URIs that are not found will return a failed {@link AppSearchResult} with a - * result code of {@link AppSearchResult#RESULT_NOT_FOUND}. - * Or {@link BatchResultCallback#onSystemError} will be invoked with a - * {@link Throwable} if an unexpected internal error occurred in AppSearch - * service. + * of the returned {@link AppSearchBatchResult} are the input document IDs. The + * values are {@code null} on success, or a failed {@link AppSearchResult} + * otherwise. IDs that are not found will return a failed + * {@link AppSearchResult} with a result code of + * {@link AppSearchResult#RESULT_NOT_FOUND}. If an unexpected internal error + * occurs in the AppSearch service, {@link BatchResultCallback#onSystemError} + * will be invoked with a {@link Throwable}. */ public void remove( - @NonNull RemoveByUriRequest request, + @NonNull RemoveByDocumentIdRequest request, @NonNull @CallbackExecutor Executor executor, @NonNull BatchResultCallback<String, Void> callback) { Objects.requireNonNull(request); @@ -516,8 +519,8 @@ public final class AppSearchSession implements Closeable { Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); try { - mService.removeByUri(mPackageName, mDatabaseName, request.getNamespace(), - new ArrayList<>(request.getUris()), mUserId, + mService.removeByDocumentId(mPackageName, mDatabaseName, request.getNamespace(), + new ArrayList<>(request.getIds()), mUserId, new IAppSearchBatchResultCallback.Stub() { @Override public void onResult(AppSearchBatchResult result) { @@ -581,8 +584,8 @@ public final class AppSearchSession implements Closeable { /** * Gets the storage info for this {@link AppSearchSession} database. * - * <p>This may take time proportional to the number of documents and may be inefficient to - * call repeatedly. + * <p>This may take time proportional to the number of documents and may be inefficient to call + * repeatedly. * * @param executor Executor on which to invoke the callback. * @param callback Callback to receive the storage info. @@ -618,8 +621,8 @@ public final class AppSearchSession implements Closeable { } /** - * Closes the {@link AppSearchSession} to persist all schema and document updates, additions, - * and deletes to disk. + * Closes the {@link AppSearchSession} to persist all schema and document updates, + * additions, and deletes to disk. */ @Override public void close() { diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java index b30ed50b8240..d49f47271055 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java @@ -31,9 +31,12 @@ import java.util.concurrent.Executor; import java.util.function.Consumer; /** - * This class provides global access to the centralized AppSearch index maintained by the system. + * Provides a connection to all AppSearch databases the querying application has been granted access + * to. * - * <p>Apps can retrieve indexed documents through the {@link #search} API. + * <p>This class is thread safe. + * + * @see AppSearchSession */ public class GlobalSearchSession implements Closeable { private static final String TAG = "AppSearchGlobalSearchSe"; @@ -151,8 +154,8 @@ public class GlobalSearchSession implements Closeable { request.getPackageName(), request.getDatabaseName(), request.getNamespace(), - request.getUri(), - request.getUsageTimeMillis(), + request.getDocumentId(), + request.getUsageTimestampMillis(), /*systemUsage=*/ true, mUserId, new IAppSearchResultCallback.Stub() { diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl index 0b8f0523ab56..17f724b3a6f5 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl @@ -100,7 +100,7 @@ interface IAppSearchManager { * will be called with the cause throwable. Otherwise, * {@link IAppSearchBatchResultCallback#onResult} will be called with an * {@link AppSearchBatchResult}<{@link String}, {@link Void}> - * where the keys are document URIs, and the values are {@code null}. + * where the keys are document IDs, and the values are {@code null}. */ void putDocuments( in String packageName, @@ -116,7 +116,7 @@ interface IAppSearchManager { * @param packageName The name of the package that owns this document. * @param databaseName The databaseName this document resides in. * @param namespace The namespace this document resides in. - * @param uris The URIs of the documents to retrieve + * @param ids The IDs of the documents to retrieve * @param typePropertyPaths A map of schema type to a list of property paths to return in the * result. * @param userId Id of the calling user @@ -125,13 +125,13 @@ interface IAppSearchManager { * will be called with the cause throwable. Otherwise, * {@link IAppSearchBatchResultCallback#onResult} will be called with an * {@link AppSearchBatchResult}<{@link String}, {@link Bundle}> - * where the keys are document URIs, and the values are Document bundles. + * where the keys are document IDs, and the values are Document bundles. */ void getDocuments( in String packageName, in String databaseName, in String namespace, - in List<String> uris, + in List<String> ids, in Map<String, List<String>> typePropertyPaths, in int userId, in IAppSearchBatchResultCallback callback); @@ -235,7 +235,7 @@ interface IAppSearchManager { in IAppSearchResultCallback callback); /** - * Reports usage of a particular document by URI and namespace. + * Reports usage of a particular document by namespace and id. * * <p>A usage report represents an event in which a user interacted with or viewed a document. * @@ -249,8 +249,8 @@ interface IAppSearchManager { * @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 id ID of the document being used. + * @param usageTimestampMillis The timestamp at which the document was used. * @param systemUsage Whether the usage was reported by a system app against another app's doc. * @param userId Id of the calling user * @param callback {@link IAppSearchResultCallback#onResult} will be called with an @@ -260,33 +260,33 @@ interface IAppSearchManager { in String packageName, in String databaseName, in String namespace, - in String uri, - in long usageTimeMillis, + in String id, + in long usageTimestampMillis, in boolean systemUsage, in int userId, in IAppSearchResultCallback callback); /** - * Removes documents by URI. + * Removes documents by ID. * * @param packageName The name of the package the document is in. * @param databaseName The databaseName the document is in. * @param namespace Namespace of the document to remove. - * @param uris The URIs of the documents to delete + * @param ids The IDs of the documents to delete * @param userId Id of the calling user * @param callback * If the call fails to start, {@link IAppSearchBatchResultCallback#onSystemError} * will be called with the cause throwable. Otherwise, * {@link IAppSearchBatchResultCallback#onResult} will be called with an * {@link AppSearchBatchResult}<{@link String}, {@link Void}> - * where the keys are document URIs. If a document doesn't exist, it will be reported as a + * where the keys are document IDs. If a document doesn't exist, it will be reported as a * failure where the {@code throwable} is {@code null}. */ - void removeByUri( + void removeByDocumentId( in String packageName, in String databaseName, in String namespace, - in List<String> uris, + in List<String> ids, in int userId, in IAppSearchBatchResultCallback callback); diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java index 77740f88de7a..d99c73f8f64b 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchEmail.java @@ -156,10 +156,10 @@ public class AppSearchEmail extends GenericDocument { * Creates a new {@link AppSearchEmail.Builder} * * @param namespace The namespace of the Email. - * @param uri The Uri of the Email. + * @param id The ID of the Email. */ - public Builder(@NonNull String namespace, @NonNull String uri) { - super(namespace, uri, SCHEMA_TYPE); + public Builder(@NonNull String namespace, @NonNull String id) { + super(namespace, id, SCHEMA_TYPE); } /** Sets the from address of {@link AppSearchEmail} */ diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java index 2368bdb7466d..2a941fb54833 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java @@ -273,7 +273,7 @@ public final class AppSearchSchema { * Returns the cardinality of the property (whether it is optional, required or repeated). */ public @Cardinality int getCardinality() { - return mBundle.getInt(CARDINALITY_FIELD, -1); + return mBundle.getInt(CARDINALITY_FIELD, CARDINALITY_OPTIONAL); } @Override @@ -407,11 +407,7 @@ public final class AppSearchSchema { return mBundle.getInt(TOKENIZER_TYPE_FIELD); } - /** - * Builder for {@link StringPropertyConfig}. - * - * <p>{@link #setCardinality} must be called or {@link #build} will fail. - */ + /** Builder for {@link StringPropertyConfig}. */ public static final class Builder { private final Bundle mBundle = new Bundle(); private boolean mBuilt = false; @@ -420,12 +416,16 @@ public final class AppSearchSchema { public Builder(@NonNull String propertyName) { mBundle.putString(NAME_FIELD, propertyName); mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_STRING); + mBundle.putInt(CARDINALITY_FIELD, CARDINALITY_OPTIONAL); + mBundle.putInt(INDEXING_TYPE_FIELD, INDEXING_TYPE_NONE); + mBundle.putInt(TOKENIZER_TYPE_FIELD, TOKENIZER_TYPE_NONE); } /** * The cardinality of the property (whether it is optional, required or repeated). * - * <p>This property must be set. + * <p>If this method is not called, the default cardinality is {@link + * PropertyConfig#CARDINALITY_OPTIONAL}. */ @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass @NonNull @@ -439,6 +439,9 @@ public final class AppSearchSchema { /** * Configures how a property should be indexed so that it can be retrieved by queries. + * + * <p>If this method is not called, the default indexing type is {@link + * StringPropertyConfig#INDEXING_TYPE_NONE}, so that it cannot be matched by queries. */ @NonNull public StringPropertyConfig.Builder setIndexingType(@IndexingType int indexingType) { @@ -449,7 +452,17 @@ public final class AppSearchSchema { return this; } - /** Configures how this property should be tokenized (split into words). */ + /** + * Configures how this property should be tokenized (split into words). + * + * <p>If this method is not called, the default indexing type is {@link + * StringPropertyConfig#TOKENIZER_TYPE_NONE}, so that it is not tokenized. + * + * <p>This method must be called with a value other than {@link + * StringPropertyConfig#TOKENIZER_TYPE_NONE} if the property is indexed (i.e. if {@link + * #setIndexingType} has been called with a value other than {@link + * StringPropertyConfig#INDEXING_TYPE_NONE}). + */ @NonNull public StringPropertyConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) { Preconditions.checkState(!mBuilt, "Builder has already been used"); @@ -464,16 +477,11 @@ public final class AppSearchSchema { * * <p>After calling this method, the builder must no longer be used. * - * @throws IllegalSchemaException if the property is not correctly populated + * @throws IllegalStateException if the builder has already been used */ @NonNull public StringPropertyConfig build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); - // TODO(b/147692920): Send the schema to Icing Lib for official validation, instead - // of partially reimplementing some of the validation Icing does here. - if (!mBundle.containsKey(CARDINALITY_FIELD)) { - throw new IllegalSchemaException("Missing field: cardinality"); - } mBuilt = true; return new StringPropertyConfig(mBundle); } @@ -486,11 +494,7 @@ public final class AppSearchSchema { super(bundle); } - /** - * Builder for {@link Int64PropertyConfig}. - * - * <p>{@link #setCardinality} must be called or {@link #build} will fail. - */ + /** Builder for {@link Int64PropertyConfig}. */ public static final class Builder { private final Bundle mBundle = new Bundle(); private boolean mBuilt = false; @@ -499,12 +503,14 @@ public final class AppSearchSchema { public Builder(@NonNull String propertyName) { mBundle.putString(NAME_FIELD, propertyName); mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_INT64); + mBundle.putInt(CARDINALITY_FIELD, CARDINALITY_OPTIONAL); } /** * The cardinality of the property (whether it is optional, required or repeated). * - * <p>This property must be set. + * <p>If this method is not called, the default cardinality is {@link + * PropertyConfig#CARDINALITY_OPTIONAL}. */ @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass @NonNull @@ -521,14 +527,11 @@ public final class AppSearchSchema { * * <p>After calling this method, the builder must no longer be used. * - * @throws IllegalSchemaException if the property is not correctly populated + * @throws IllegalStateException if the builder has already been used */ @NonNull public Int64PropertyConfig build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); - if (!mBundle.containsKey(CARDINALITY_FIELD)) { - throw new IllegalSchemaException("Missing field: cardinality"); - } mBuilt = true; return new Int64PropertyConfig(mBundle); } @@ -541,11 +544,7 @@ public final class AppSearchSchema { super(bundle); } - /** - * Builder for {@link DoublePropertyConfig}. - * - * <p>{@link #setCardinality} must be called or {@link #build} will fail. - */ + /** Builder for {@link DoublePropertyConfig}. */ public static final class Builder { private final Bundle mBundle = new Bundle(); private boolean mBuilt = false; @@ -554,12 +553,14 @@ public final class AppSearchSchema { public Builder(@NonNull String propertyName) { mBundle.putString(NAME_FIELD, propertyName); mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_DOUBLE); + mBundle.putInt(CARDINALITY_FIELD, CARDINALITY_OPTIONAL); } /** * The cardinality of the property (whether it is optional, required or repeated). * - * <p>This property must be set. + * <p>If this method is not called, the default cardinality is {@link + * PropertyConfig#CARDINALITY_OPTIONAL}. */ @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass @NonNull @@ -576,14 +577,11 @@ public final class AppSearchSchema { * * <p>After calling this method, the builder must no longer be used. * - * @throws IllegalSchemaException if the property is not correctly populated + * @throws IllegalStateException if the builder has already been used */ @NonNull public DoublePropertyConfig build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); - if (!mBundle.containsKey(CARDINALITY_FIELD)) { - throw new IllegalSchemaException("Missing field: cardinality"); - } mBuilt = true; return new DoublePropertyConfig(mBundle); } @@ -596,11 +594,7 @@ public final class AppSearchSchema { super(bundle); } - /** - * Builder for {@link BooleanPropertyConfig}. - * - * <p>{@link #setCardinality} must be called or {@link #build} will fail. - */ + /** Builder for {@link BooleanPropertyConfig}. */ public static final class Builder { private final Bundle mBundle = new Bundle(); private boolean mBuilt = false; @@ -609,12 +603,14 @@ public final class AppSearchSchema { public Builder(@NonNull String propertyName) { mBundle.putString(NAME_FIELD, propertyName); mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_BOOLEAN); + mBundle.putInt(CARDINALITY_FIELD, CARDINALITY_OPTIONAL); } /** * The cardinality of the property (whether it is optional, required or repeated). * - * <p>This property must be set. + * <p>If this method is not called, the default cardinality is {@link + * PropertyConfig#CARDINALITY_OPTIONAL}. */ @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass @NonNull @@ -631,14 +627,11 @@ public final class AppSearchSchema { * * <p>After calling this method, the builder must no longer be used. * - * @throws IllegalSchemaException if the property is not correctly populated + * @throws IllegalStateException if the builder has already been used */ @NonNull public BooleanPropertyConfig build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); - if (!mBundle.containsKey(CARDINALITY_FIELD)) { - throw new IllegalSchemaException("Missing field: cardinality"); - } mBuilt = true; return new BooleanPropertyConfig(mBundle); } @@ -651,11 +644,7 @@ public final class AppSearchSchema { super(bundle); } - /** - * Builder for {@link BytesPropertyConfig}. - * - * <p>{@link #setCardinality} must be called or {@link #build} will fail. - */ + /** Builder for {@link BytesPropertyConfig}. */ public static final class Builder { private final Bundle mBundle = new Bundle(); private boolean mBuilt = false; @@ -664,12 +653,14 @@ public final class AppSearchSchema { public Builder(@NonNull String propertyName) { mBundle.putString(NAME_FIELD, propertyName); mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_BYTES); + mBundle.putInt(CARDINALITY_FIELD, CARDINALITY_OPTIONAL); } /** * The cardinality of the property (whether it is optional, required or repeated). * - * <p>This property must be set. + * <p>If this method is not called, the default cardinality is {@link + * PropertyConfig#CARDINALITY_OPTIONAL}. */ @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass @NonNull @@ -686,14 +677,11 @@ public final class AppSearchSchema { * * <p>After calling this method, the builder must no longer be used. * - * @throws IllegalSchemaException if the property is not correctly populated + * @throws IllegalStateException if the builder has already been used */ @NonNull public BytesPropertyConfig build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); - if (!mBundle.containsKey(CARDINALITY_FIELD)) { - throw new IllegalSchemaException("Missing field: cardinality"); - } mBuilt = true; return new BytesPropertyConfig(mBundle); } @@ -722,7 +710,7 @@ public final class AppSearchSchema { * <p>If false, the nested document's properties are not indexed regardless of its own * schema. */ - public boolean isIndexNestedProperties() { + public boolean shouldIndexNestedProperties() { return mBundle.getBoolean(INDEX_NESTED_PROPERTIES_FIELD); } @@ -741,29 +729,28 @@ public final class AppSearchSchema { private final Bundle mBundle = new Bundle(); private boolean mBuilt = false; - /** Creates a new {@link DocumentPropertyConfig.Builder}. */ - public Builder(@NonNull String propertyName) { - mBundle.putString(NAME_FIELD, propertyName); - mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_DOCUMENT); - } - /** - * The logical schema-type of the contents of this property. + * Creates a new {@link DocumentPropertyConfig.Builder}. * - * <p>This property must be set. + * @param propertyName The logical name of the property in the schema, which will be + * used as the key for this property in {@link + * GenericDocument.Builder#setPropertyDocument}. + * @param schemaType The type of documents which will be stored in this property. + * Documents of different types cannot be mixed into a single property. */ - @NonNull - public DocumentPropertyConfig.Builder setSchemaType(@NonNull String schemaType) { - Preconditions.checkState(!mBuilt, "Builder has already been used"); - Objects.requireNonNull(schemaType); + public Builder(@NonNull String propertyName, @NonNull String schemaType) { + mBundle.putString(NAME_FIELD, propertyName); + mBundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_DOCUMENT); + mBundle.putInt(CARDINALITY_FIELD, CARDINALITY_OPTIONAL); + mBundle.putBoolean(INDEX_NESTED_PROPERTIES_FIELD, false); mBundle.putString(SCHEMA_TYPE_FIELD, schemaType); - return this; } /** * The cardinality of the property (whether it is optional, required or repeated). * - * <p>This property must be set. + * <p>If this method is not called, the default cardinality is {@link + * PropertyConfig#CARDINALITY_OPTIONAL}. */ @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass @NonNull @@ -783,7 +770,7 @@ public final class AppSearchSchema { * schema. */ @NonNull - public DocumentPropertyConfig.Builder setIndexNestedProperties( + public DocumentPropertyConfig.Builder setShouldIndexNestedProperties( boolean indexNestedProperties) { Preconditions.checkState(!mBuilt, "Builder has already been used"); mBundle.putBoolean(INDEX_NESTED_PROPERTIES_FIELD, indexNestedProperties); @@ -795,18 +782,12 @@ public final class AppSearchSchema { * * <p>After calling this method, the builder must no longer be used. * - * @throws IllegalSchemaException If the property is not correctly populated (e.g. - * missing {@code dataType}). + * @throws IllegalStateException if the builder has already been used (e.g. missing + * {@code dataType}). */ @NonNull public DocumentPropertyConfig build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); - if (mBundle.getString(SCHEMA_TYPE_FIELD, "").isEmpty()) { - throw new IllegalSchemaException("Missing field: schemaType"); - } - if (!mBundle.containsKey(CARDINALITY_FIELD)) { - throw new IllegalSchemaException("Missing field: cardinality"); - } mBuilt = true; return new DocumentPropertyConfig(mBundle); } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java index e3b3a859981d..85018ad6e9db 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java @@ -16,6 +16,7 @@ package android.app.appsearch; +import android.annotation.CurrentTimeMillisLong; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -31,6 +32,7 @@ import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.Set; @@ -38,10 +40,12 @@ import java.util.Set; * Represents a document unit. * * <p>Documents contain structured data conforming to their {@link AppSearchSchema} type. Each - * document is uniquely identified by a URI and namespace. + * document is uniquely identified by a namespace and a String ID within that namespace. + * + * <p>Documents are constructed by using the {@link GenericDocument.Builder}. * * @see AppSearchSession#put - * @see AppSearchSession#getByUri + * @see AppSearchSession#getByDocumentId * @see AppSearchSession#search */ public class GenericDocument { @@ -65,7 +69,7 @@ public class GenericDocument { private static final String PROPERTIES_FIELD = "properties"; private static final String BYTE_ARRAY_FIELD = "byteArray"; private static final String SCHEMA_TYPE_FIELD = "schemaType"; - private static final String URI_FIELD = "uri"; + private static final String ID_FIELD = "id"; private static final String SCORE_FIELD = "score"; private static final String TTL_MILLIS_FIELD = "ttlMillis"; private static final String CREATION_TIMESTAMP_MILLIS_FIELD = "creationTimestampMillis"; @@ -82,30 +86,33 @@ public class GenericDocument { return MAX_INDEXED_PROPERTIES; } - /** Contains {@link GenericDocument} basic information (uri, schemaType etc). */ + /** + * Contains all {@link GenericDocument} information in a packaged format. + * + * <p>Keys are the {@code *_FIELD} constants in this class. + */ @NonNull final Bundle mBundle; - /** Contains all properties in {@link GenericDocument} to support getting properties via keys */ + /** Contains all properties in {@link GenericDocument} to support getting properties via name */ @NonNull private final Bundle mProperties; - @NonNull private final String mUri; + @NonNull private final String mId; @NonNull private final String mSchemaType; private final long mCreationTimestampMillis; @Nullable private Integer mHashCode; /** - * Rebuilds a {@link GenericDocument} by the a bundle. + * Rebuilds a {@link GenericDocument} from a bundle. * - * @param bundle Contains {@link GenericDocument} basic information (uri, schemaType etc) and a - * properties bundle contains all properties in {@link GenericDocument} to support getting - * properties via keys. + * @param bundle Packaged {@link GenericDocument} data, such as the result of {@link + * #getBundle}. * @hide */ public GenericDocument(@NonNull Bundle bundle) { Objects.requireNonNull(bundle); mBundle = bundle; mProperties = Objects.requireNonNull(bundle.getParcelable(PROPERTIES_FIELD)); - mUri = Objects.requireNonNull(mBundle.getString(URI_FIELD)); + mId = Objects.requireNonNull(mBundle.getString(ID_FIELD)); mSchemaType = Objects.requireNonNull(mBundle.getString(SCHEMA_TYPE_FIELD)); mCreationTimestampMillis = mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis()); @@ -130,10 +137,10 @@ public class GenericDocument { return mBundle; } - /** Returns the URI of the {@link GenericDocument}. */ + /** Returns the unique identifier of the {@link GenericDocument}. */ @NonNull - public String getUri() { - return mUri; + public String getId() { + return mId; } /** Returns the namespace of the {@link GenericDocument}. */ @@ -153,6 +160,7 @@ public class GenericDocument { * * <p>The value is in the {@link System#currentTimeMillis} time base. */ + @CurrentTimeMillisLong public long getCreationTimestampMillis() { return mCreationTimestampMillis; } @@ -193,136 +201,517 @@ public class GenericDocument { } /** - * Retrieves the property value with the given key as {@link Object}. + * Retrieves the property value with the given path as {@link Object}. + * + * <p>A path can be a simple property name, such as those returned by {@link #getPropertyNames}. + * It may also be a dot-delimited path through the nested document hierarchy, with nested {@link + * GenericDocument} properties accessed via {@code '.'} and repeated properties optionally + * indexed into via {@code [n]}. + * + * <p>For example, given the following {@link GenericDocument}: + * + * <pre> + * (Message) { + * from: "sender@example.com" + * to: [{ + * name: "Albert Einstein" + * email: "einstein@example.com" + * }, { + * name: "Marie Curie" + * email: "curie@example.com" + * }] + * tags: ["important", "inbox"] + * subject: "Hello" + * } + * </pre> + * + * <p>Here are some example paths and their results: + * + * <ul> + * <li>{@code "from"} returns {@code "sender@example.com"} as a {@link String} array with one + * element + * <li>{@code "to"} returns the two nested documents containing contact information as a + * {@link GenericDocument} array with two elements + * <li>{@code "to[1]"} returns the second nested document containing Marie Curie's contact + * information as a {@link GenericDocument} array with one element + * <li>{@code "to[1].email"} returns {@code "curie@example.com"} + * <li>{@code "to[100].email"} returns {@code null} as this particular document does not have + * that many elements in its {@code "to"} array. + * <li>{@code "to.email"} aggregates emails across all nested documents that have them, + * returning {@code ["einstein@example.com", "curie@example.com"]} as a {@link String} + * array with two elements. + * </ul> + * + * <p>If you know the expected type of the property you are retrieving, it is recommended to use + * one of the typed versions of this method instead, such as {@link #getPropertyString} or + * {@link #getPropertyStringArray}. + * + * @param path The path to look for. + * @return The entry with the given path as an object or {@code null} if there is no such path. + * The returned object will be one of the following types: {@code String[]}, {@code long[]}, + * {@code double[]}, {@code boolean[]}, {@code byte[][]}, {@code GenericDocument[]}. + */ + @Nullable + public Object getProperty(@NonNull String path) { + Objects.requireNonNull(path); + Object rawValue = getRawPropertyFromRawDocument(path, mBundle); + + // Unpack the raw value into the types the user expects, if required. + if (rawValue instanceof Bundle) { + // getRawPropertyFromRawDocument may return a document as a bare Bundle as a performance + // optimization for lookups. + GenericDocument document = new GenericDocument((Bundle) rawValue); + return new GenericDocument[] {document}; + } + + if (rawValue instanceof List) { + // byte[][] fields are packed into List<Bundle> where each Bundle contains just a single + // entry: BYTE_ARRAY_FIELD -> byte[]. + @SuppressWarnings("unchecked") + List<Bundle> bundles = (List<Bundle>) rawValue; + if (bundles.size() == 0) { + return null; + } + byte[][] bytes = new byte[bundles.size()][]; + for (int i = 0; i < bundles.size(); i++) { + Bundle bundle = bundles.get(i); + if (bundle == null) { + Log.e(TAG, "The inner bundle is null at " + i + ", for path: " + path); + continue; + } + byte[] innerBytes = bundle.getByteArray(BYTE_ARRAY_FIELD); + if (innerBytes == null) { + Log.e(TAG, "The bundle at " + i + " contains a null byte[]."); + continue; + } + bytes[i] = innerBytes; + } + return bytes; + } + + if (rawValue instanceof Parcelable[]) { + // The underlying Bundle of nested GenericDocuments is packed into a Parcelable array. + // We must unpack it into GenericDocument instances. + Parcelable[] bundles = (Parcelable[]) rawValue; + if (bundles.length == 0) { + return null; + } + GenericDocument[] documents = new GenericDocument[bundles.length]; + for (int i = 0; i < bundles.length; i++) { + if (bundles[i] == null) { + Log.e(TAG, "The inner bundle is null at " + i + ", for path: " + path); + continue; + } + if (!(bundles[i] instanceof Bundle)) { + Log.e( + TAG, + "The inner element at " + + i + + " is a " + + bundles[i].getClass() + + ", not a Bundle for path: " + + path); + continue; + } + documents[i] = new GenericDocument((Bundle) bundles[i]); + } + return documents; + } + + // Otherwise the raw property is the same as the final property and needs no transformation. + return rawValue; + } + + /** + * Looks up a property path within the given document bundle. + * + * <p>The return value may be any of GenericDocument's internal repeated storage types + * (String[], long[], double[], boolean[], ArrayList<Bundle>, Parcelable[]). + */ + @Nullable + private static Object getRawPropertyFromRawDocument( + @NonNull String path, @NonNull Bundle documentBundle) { + Objects.requireNonNull(path); + Objects.requireNonNull(documentBundle); + Bundle properties = Objects.requireNonNull(documentBundle.getBundle(PROPERTIES_FIELD)); + + // Determine whether the path is just a raw property name with no control characters + int controlIdx = -1; + boolean controlIsIndex = false; + for (int i = 0; i < path.length(); i++) { + char c = path.charAt(i); + if (c == '[' || c == '.') { + controlIdx = i; + controlIsIndex = c == '['; + break; + } + } + + // Look up the value of the first path element + Object firstElementValue; + if (controlIdx == -1) { + firstElementValue = properties.get(path); + } else { + String name = path.substring(0, controlIdx); + firstElementValue = properties.get(name); + } + + // If the path has no further elements, we're done. + if (firstElementValue == null || controlIdx == -1) { + return firstElementValue; + } + + // At this point, for a path like "recipients[0]", firstElementValue contains the value of + // "recipients". If the first element of the path is an indexed value, we now update + // firstElementValue to contain "recipients[0]" instead. + String remainingPath; + if (!controlIsIndex) { + // Remaining path is everything after the . + remainingPath = path.substring(controlIdx + 1); + } else { + int endBracketIdx = path.indexOf(']', controlIdx); + if (endBracketIdx == -1) { + throw new IllegalArgumentException("Malformed path (no ending ']'): " + path); + } + if (endBracketIdx + 1 < path.length() && path.charAt(endBracketIdx + 1) != '.') { + throw new IllegalArgumentException( + "Malformed path (']' not followed by '.'): " + path); + } + String indexStr = path.substring(controlIdx + 1, endBracketIdx); + int index = Integer.parseInt(indexStr); + if (index < 0) { + throw new IllegalArgumentException("Path index less than 0: " + path); + } + + // Remaining path is everything after the [n] + if (endBracketIdx + 1 < path.length()) { + // More path remains, and we've already checked that charAt(endBracketIdx+1) == . + remainingPath = path.substring(endBracketIdx + 2); + } else { + // No more path remains. + remainingPath = null; + } + + // Extract the right array element + Object extractedValue = null; + if (firstElementValue instanceof String[]) { + String[] stringValues = (String[]) firstElementValue; + if (index < stringValues.length) { + extractedValue = Arrays.copyOfRange(stringValues, index, index + 1); + } + } else if (firstElementValue instanceof long[]) { + long[] longValues = (long[]) firstElementValue; + if (index < longValues.length) { + extractedValue = Arrays.copyOfRange(longValues, index, index + 1); + } + } else if (firstElementValue instanceof double[]) { + double[] doubleValues = (double[]) firstElementValue; + if (index < doubleValues.length) { + extractedValue = Arrays.copyOfRange(doubleValues, index, index + 1); + } + } else if (firstElementValue instanceof boolean[]) { + boolean[] booleanValues = (boolean[]) firstElementValue; + if (index < booleanValues.length) { + extractedValue = Arrays.copyOfRange(booleanValues, index, index + 1); + } + } else if (firstElementValue instanceof List) { + @SuppressWarnings("unchecked") + List<Bundle> bundles = (List<Bundle>) firstElementValue; + if (index < bundles.size()) { + extractedValue = bundles.subList(index, index + 1); + } + } else if (firstElementValue instanceof Parcelable[]) { + // Special optimization: to avoid creating new singleton arrays for traversing paths + // we return the bare document Bundle in this particular case. + Parcelable[] bundles = (Parcelable[]) firstElementValue; + if (index < bundles.length) { + extractedValue = (Bundle) bundles[index]; + } + } else { + throw new IllegalStateException("Unsupported value type: " + firstElementValue); + } + firstElementValue = extractedValue; + } + + // If we are at the end of the path or there are no deeper elements in this document, we + // have nothing to recurse into. + if (firstElementValue == null || remainingPath == null) { + return firstElementValue; + } + + // More of the path remains; recursively evaluate it + if (firstElementValue instanceof Bundle) { + return getRawPropertyFromRawDocument(remainingPath, (Bundle) firstElementValue); + } else if (firstElementValue instanceof Parcelable[]) { + Parcelable[] parcelables = (Parcelable[]) firstElementValue; + if (parcelables.length == 1) { + return getRawPropertyFromRawDocument(remainingPath, (Bundle) parcelables[0]); + } + + // Slowest path: we're collecting values across repeated nested docs. (Example: given a + // path like recipient.name, where recipient is a repeated field, we return a string + // array where each recipient's name is an array element). + // + // Performance note: Suppose that we have a property path "a.b.c" where the "a" + // property has N document values and each containing a "b" property with M document + // values and each of those containing a "c" property with an int array. + // + // We'll allocate a new ArrayList for each of the "b" properties, add the M int arrays + // from the "c" properties to it and then we'll allocate an int array in + // flattenAccumulator before returning that (1 + M allocation per "b" property). + // + // When we're on the "a" properties, we'll allocate an ArrayList and add the N + // flattened int arrays returned from the "b" properties to the list. Then we'll + // allocate an int array in flattenAccumulator (1 + N ("b" allocs) allocations per "a"). + // So this implementation could incur 1 + N + NM allocs. + // + // However, we expect the vast majority of getProperty calls to be either for direct + // property names (not paths) or else property paths returned from snippetting, which + // always refer to exactly one property value and don't aggregate across repeated + // values. The implementation is optimized for these two cases, requiring no additional + // allocations. So we've decided that the above performance characteristics are OK for + // the less used path. + List<Object> accumulator = new ArrayList<>(parcelables.length); + for (int i = 0; i < parcelables.length; i++) { + Object value = + getRawPropertyFromRawDocument(remainingPath, (Bundle) parcelables[i]); + if (value != null) { + accumulator.add(value); + } + } + return flattenAccumulator(accumulator); + } else { + Log.e(TAG, "Failed to apply path to document; no nested value found: " + path); + return null; + } + } + + /** + * Combines accumulated repeated properties from multiple documents into a single array. * - * @param key The key to look for. - * @return The entry with the given key as an object or {@code null} if there is no such key. + * @param accumulator List containing objects of the following types: {@code String[]}, {@code + * long[]}, {@code double[]}, {@code boolean[]}, {@code List<Bundle>}, or {@code + * Parcelable[]}. + * @return The result of concatenating each individual list element into a larger array/list of + * the same type. */ @Nullable - public Object getProperty(@NonNull String key) { - Objects.requireNonNull(key); - Object property = mProperties.get(key); - if (property instanceof ArrayList) { - return getPropertyBytesArray(key); - } else if (property instanceof Parcelable[]) { - return getPropertyDocumentArray(key); + private static Object flattenAccumulator(@NonNull List<Object> accumulator) { + if (accumulator.isEmpty()) { + return null; } - return property; + Object first = accumulator.get(0); + if (first instanceof String[]) { + int length = 0; + for (int i = 0; i < accumulator.size(); i++) { + length += ((String[]) accumulator.get(i)).length; + } + String[] result = new String[length]; + int total = 0; + for (int i = 0; i < accumulator.size(); i++) { + String[] castValue = (String[]) accumulator.get(i); + System.arraycopy(castValue, 0, result, total, castValue.length); + total += castValue.length; + } + return result; + } + if (first instanceof long[]) { + int length = 0; + for (int i = 0; i < accumulator.size(); i++) { + length += ((long[]) accumulator.get(i)).length; + } + long[] result = new long[length]; + int total = 0; + for (int i = 0; i < accumulator.size(); i++) { + long[] castValue = (long[]) accumulator.get(i); + System.arraycopy(castValue, 0, result, total, castValue.length); + total += castValue.length; + } + return result; + } + if (first instanceof double[]) { + int length = 0; + for (int i = 0; i < accumulator.size(); i++) { + length += ((double[]) accumulator.get(i)).length; + } + double[] result = new double[length]; + int total = 0; + for (int i = 0; i < accumulator.size(); i++) { + double[] castValue = (double[]) accumulator.get(i); + System.arraycopy(castValue, 0, result, total, castValue.length); + total += castValue.length; + } + return result; + } + if (first instanceof boolean[]) { + int length = 0; + for (int i = 0; i < accumulator.size(); i++) { + length += ((boolean[]) accumulator.get(i)).length; + } + boolean[] result = new boolean[length]; + int total = 0; + for (int i = 0; i < accumulator.size(); i++) { + boolean[] castValue = (boolean[]) accumulator.get(i); + System.arraycopy(castValue, 0, result, total, castValue.length); + total += castValue.length; + } + return result; + } + if (first instanceof List) { + int length = 0; + for (int i = 0; i < accumulator.size(); i++) { + length += ((List<?>) accumulator.get(i)).size(); + } + List<Bundle> result = new ArrayList<>(length); + for (int i = 0; i < accumulator.size(); i++) { + @SuppressWarnings("unchecked") + List<Bundle> castValue = (List<Bundle>) accumulator.get(i); + result.addAll(castValue); + } + return result; + } + if (first instanceof Parcelable[]) { + int length = 0; + for (int i = 0; i < accumulator.size(); i++) { + length += ((Parcelable[]) accumulator.get(i)).length; + } + Parcelable[] result = new Parcelable[length]; + int total = 0; + for (int i = 0; i < accumulator.size(); i++) { + Parcelable[] castValue = (Parcelable[]) accumulator.get(i); + System.arraycopy(castValue, 0, result, total, castValue.length); + total += castValue.length; + } + return result; + } + throw new IllegalStateException("Unexpected property type: " + first); } /** - * Retrieves a {@link String} value by key. + * Retrieves a {@link String} property by path. * - * @param key The key to look for. - * @return The first {@link String} associated with the given key or {@code null} if there is no - * such key or the value is of a different type. + * <p>See {@link #getProperty} for a detailed description of the path syntax. + * + * @param path The path to look for. + * @return The first {@link String} associated with the given path or {@code null} if there is + * no such value or the value is of a different type. */ @Nullable - public String getPropertyString(@NonNull String key) { - Objects.requireNonNull(key); - String[] propertyArray = getPropertyStringArray(key); + public String getPropertyString(@NonNull String path) { + Objects.requireNonNull(path); + String[] propertyArray = getPropertyStringArray(path); if (propertyArray == null || propertyArray.length == 0) { return null; } - warnIfSinglePropertyTooLong("String", key, propertyArray.length); + warnIfSinglePropertyTooLong("String", path, propertyArray.length); return propertyArray[0]; } /** - * Retrieves a {@code long} value by key. + * Retrieves a {@code long} property by path. + * + * <p>See {@link #getProperty} for a detailed description of the path syntax. * - * @param key The key to look for. - * @return The first {@code long} associated with the given key or default value {@code 0} if - * there is no such key or the value is of a different type. + * @param path The path to look for. + * @return The first {@code long} associated with the given path or default value {@code 0} if + * there is no such value or the value is of a different type. */ - public long getPropertyLong(@NonNull String key) { - Objects.requireNonNull(key); - long[] propertyArray = getPropertyLongArray(key); + public long getPropertyLong(@NonNull String path) { + Objects.requireNonNull(path); + long[] propertyArray = getPropertyLongArray(path); if (propertyArray == null || propertyArray.length == 0) { return 0; } - warnIfSinglePropertyTooLong("Long", key, propertyArray.length); + warnIfSinglePropertyTooLong("Long", path, propertyArray.length); return propertyArray[0]; } /** - * Retrieves a {@code double} value by key. + * Retrieves a {@code double} property by path. + * + * <p>See {@link #getProperty} for a detailed description of the path syntax. * - * @param key The key to look for. - * @return The first {@code double} associated with the given key or default value {@code 0.0} - * if there is no such key or the value is of a different type. + * @param path The path to look for. + * @return The first {@code double} associated with the given path or default value {@code 0.0} + * if there is no such value or the value is of a different type. */ - public double getPropertyDouble(@NonNull String key) { - Objects.requireNonNull(key); - double[] propertyArray = getPropertyDoubleArray(key); + public double getPropertyDouble(@NonNull String path) { + Objects.requireNonNull(path); + double[] propertyArray = getPropertyDoubleArray(path); if (propertyArray == null || propertyArray.length == 0) { return 0.0; } - warnIfSinglePropertyTooLong("Double", key, propertyArray.length); + warnIfSinglePropertyTooLong("Double", path, propertyArray.length); return propertyArray[0]; } /** - * Retrieves a {@code boolean} value by key. + * Retrieves a {@code boolean} property by path. * - * @param key The key to look for. - * @return The first {@code boolean} associated with the given key or default value {@code - * false} if there is no such key or the value is of a different type. + * <p>See {@link #getProperty} for a detailed description of the path syntax. + * + * @param path The path to look for. + * @return The first {@code boolean} associated with the given path or default value {@code + * false} if there is no such value or the value is of a different type. */ - public boolean getPropertyBoolean(@NonNull String key) { - Objects.requireNonNull(key); - boolean[] propertyArray = getPropertyBooleanArray(key); + public boolean getPropertyBoolean(@NonNull String path) { + Objects.requireNonNull(path); + boolean[] propertyArray = getPropertyBooleanArray(path); if (propertyArray == null || propertyArray.length == 0) { return false; } - warnIfSinglePropertyTooLong("Boolean", key, propertyArray.length); + warnIfSinglePropertyTooLong("Boolean", path, propertyArray.length); return propertyArray[0]; } /** - * Retrieves a {@code byte[]} value by key. + * Retrieves a {@code byte[]} property by path. + * + * <p>See {@link #getProperty} for a detailed description of the path syntax. * - * @param key The key to look for. - * @return The first {@code byte[]} associated with the given key or {@code null} if there is no - * such key or the value is of a different type. + * @param path The path to look for. + * @return The first {@code byte[]} associated with the given path or {@code null} if there is + * no such value or the value is of a different type. */ @Nullable - public byte[] getPropertyBytes(@NonNull String key) { - Objects.requireNonNull(key); - byte[][] propertyArray = getPropertyBytesArray(key); + public byte[] getPropertyBytes(@NonNull String path) { + Objects.requireNonNull(path); + byte[][] propertyArray = getPropertyBytesArray(path); if (propertyArray == null || propertyArray.length == 0) { return null; } - warnIfSinglePropertyTooLong("ByteArray", key, propertyArray.length); + warnIfSinglePropertyTooLong("ByteArray", path, propertyArray.length); return propertyArray[0]; } /** - * Retrieves a {@link GenericDocument} value by key. + * Retrieves a {@link GenericDocument} property by path. * - * @param key The key to look for. - * @return The first {@link GenericDocument} associated with the given key or {@code null} if - * there is no such key or the value is of a different type. + * <p>See {@link #getProperty} for a detailed description of the path syntax. + * + * @param path The path to look for. + * @return The first {@link GenericDocument} associated with the given path or {@code null} if + * there is no such value or the value is of a different type. */ @Nullable - public GenericDocument getPropertyDocument(@NonNull String key) { - Objects.requireNonNull(key); - GenericDocument[] propertyArray = getPropertyDocumentArray(key); + public GenericDocument getPropertyDocument(@NonNull String path) { + Objects.requireNonNull(path); + GenericDocument[] propertyArray = getPropertyDocumentArray(path); if (propertyArray == null || propertyArray.length == 0) { return null; } - warnIfSinglePropertyTooLong("Document", key, propertyArray.length); + warnIfSinglePropertyTooLong("Document", path, propertyArray.length); return propertyArray[0]; } /** Prints a warning to logcat if the given propertyLength is greater than 1. */ private static void warnIfSinglePropertyTooLong( - @NonNull String propertyType, @NonNull String key, int propertyLength) { + @NonNull String propertyType, @NonNull String path, int propertyLength) { if (propertyLength > 1) { Log.w( TAG, "The value for \"" - + key + + path + "\" contains " + propertyLength + " elements. Only the first one will be returned from " @@ -335,141 +724,121 @@ public class GenericDocument { } /** - * Retrieves a repeated {@code String} property by key. + * Retrieves a repeated {@code String} property by path. + * + * <p>See {@link #getProperty} for a detailed description of the path syntax. * - * @param key The key to look for. - * @return The {@code String[]} associated with the given key, or {@code null} if no value is + * @param path The path to look for. + * @return The {@code String[]} associated with the given path, or {@code null} if no value is * set or the value is of a different type. */ @Nullable - public String[] getPropertyStringArray(@NonNull String key) { - Objects.requireNonNull(key); - return getAndCastPropertyArray(key, String[].class); + public String[] getPropertyStringArray(@NonNull String path) { + Objects.requireNonNull(path); + Object value = getProperty(path); + return safeCastProperty(path, value, String[].class); } /** - * Retrieves a repeated {@code long[]} property by key. + * Retrieves a repeated {@code long[]} property by path. * - * @param key The key to look for. - * @return The {@code long[]} associated with the given key, or {@code null} if no value is set + * <p>See {@link #getProperty} for a detailed description of the path syntax. + * + * @param path The path to look for. + * @return The {@code long[]} associated with the given path, or {@code null} if no value is set * or the value is of a different type. */ @Nullable - public long[] getPropertyLongArray(@NonNull String key) { - Objects.requireNonNull(key); - return getAndCastPropertyArray(key, long[].class); + public long[] getPropertyLongArray(@NonNull String path) { + Objects.requireNonNull(path); + Object value = getProperty(path); + return safeCastProperty(path, value, long[].class); } /** - * Retrieves a repeated {@code double} property by key. + * Retrieves a repeated {@code double} property by path. + * + * <p>See {@link #getProperty} for a detailed description of the path syntax. * - * @param key The key to look for. - * @return The {@code double[]} associated with the given key, or {@code null} if no value is + * @param path The path to look for. + * @return The {@code double[]} associated with the given path, or {@code null} if no value is * set or the value is of a different type. */ @Nullable - public double[] getPropertyDoubleArray(@NonNull String key) { - Objects.requireNonNull(key); - return getAndCastPropertyArray(key, double[].class); + public double[] getPropertyDoubleArray(@NonNull String path) { + Objects.requireNonNull(path); + Object value = getProperty(path); + return safeCastProperty(path, value, double[].class); } /** - * Retrieves a repeated {@code boolean} property by key. + * Retrieves a repeated {@code boolean} property by path. + * + * <p>See {@link #getProperty} for a detailed description of the path syntax. * - * @param key The key to look for. - * @return The {@code boolean[]} associated with the given key, or {@code null} if no value is + * @param path The path to look for. + * @return The {@code boolean[]} associated with the given path, or {@code null} if no value is * set or the value is of a different type. */ @Nullable - public boolean[] getPropertyBooleanArray(@NonNull String key) { - Objects.requireNonNull(key); - return getAndCastPropertyArray(key, boolean[].class); + public boolean[] getPropertyBooleanArray(@NonNull String path) { + Objects.requireNonNull(path); + Object value = getProperty(path); + return safeCastProperty(path, value, boolean[].class); } /** - * Retrieves a {@code byte[][]} property by key. + * Retrieves a {@code byte[][]} property by path. * - * @param key The key to look for. - * @return The {@code byte[][]} associated with the given key, or {@code null} if no value is + * <p>See {@link #getProperty} for a detailed description of the path syntax. + * + * @param path The path to look for. + * @return The {@code byte[][]} associated with the given path, or {@code null} if no value is * set or the value is of a different type. */ @SuppressLint("ArrayReturn") @Nullable - @SuppressWarnings("unchecked") - public byte[][] getPropertyBytesArray(@NonNull String key) { - Objects.requireNonNull(key); - ArrayList<Bundle> bundles = getAndCastPropertyArray(key, ArrayList.class); - if (bundles == null || bundles.size() == 0) { - return null; - } - byte[][] bytes = new byte[bundles.size()][]; - for (int i = 0; i < bundles.size(); i++) { - Bundle bundle = bundles.get(i); - if (bundle == null) { - Log.e(TAG, "The inner bundle is null at " + i + ", for key: " + key); - continue; - } - byte[] innerBytes = bundle.getByteArray(BYTE_ARRAY_FIELD); - if (innerBytes == null) { - Log.e(TAG, "The bundle at " + i + " contains a null byte[]."); - continue; - } - bytes[i] = innerBytes; - } - return bytes; + public byte[][] getPropertyBytesArray(@NonNull String path) { + Objects.requireNonNull(path); + Object value = getProperty(path); + return safeCastProperty(path, value, byte[][].class); } /** - * Retrieves a repeated {@link GenericDocument} property by key. + * Retrieves a repeated {@link GenericDocument} property by path. + * + * <p>See {@link #getProperty} for a detailed description of the path syntax. * - * @param key The key to look for. - * @return The {@link GenericDocument}[] associated with the given key, or {@code null} if no + * @param path The path to look for. + * @return The {@link GenericDocument}[] associated with the given path, or {@code null} if no * value is set or the value is of a different type. */ @SuppressLint("ArrayReturn") @Nullable - public GenericDocument[] getPropertyDocumentArray(@NonNull String key) { - Objects.requireNonNull(key); - Parcelable[] bundles = getAndCastPropertyArray(key, Parcelable[].class); - if (bundles == null || bundles.length == 0) { - return null; - } - GenericDocument[] documents = new GenericDocument[bundles.length]; - for (int i = 0; i < bundles.length; i++) { - if (bundles[i] == null) { - Log.e(TAG, "The inner bundle is null at " + i + ", for key: " + key); - continue; - } - if (!(bundles[i] instanceof Bundle)) { - Log.e( - TAG, - "The inner element at " - + i - + " is a " - + bundles[i].getClass() - + ", not a Bundle for key: " - + key); - continue; - } - documents[i] = new GenericDocument((Bundle) bundles[i]); - } - return documents; + public GenericDocument[] getPropertyDocumentArray(@NonNull String path) { + Objects.requireNonNull(path); + Object value = getProperty(path); + return safeCastProperty(path, value, GenericDocument[].class); } /** - * Gets a repeated property of the given key, and casts it to the given class type, which must - * be an array class type. + * Casts a repeated property to the provided type, logging an error and returning {@code null} + * if the cast fails. + * + * @param path Path to the property within the document. Used for logging. + * @param value Value of the property + * @param tClass Class to cast the value into */ @Nullable - private <T> T getAndCastPropertyArray(@NonNull String key, @NonNull Class<T> tClass) { - Object value = mProperties.get(key); + private static <T> T safeCastProperty( + @NonNull String path, @Nullable Object value, @NonNull Class<T> tClass) { if (value == null) { return null; } try { return tClass.cast(value); } catch (ClassCastException e) { - Log.w(TAG, "Error casting to requested type for key \"" + key + "\"", e); + Log.w(TAG, "Error casting to requested type for path \"" + path + "\"", e); return null; } } @@ -500,17 +869,15 @@ public class GenericDocument { return bundleToString(mBundle).toString(); } - @SuppressWarnings("unchecked") private static StringBuilder bundleToString(Bundle bundle) { StringBuilder stringBuilder = new StringBuilder(); try { - final Set<String> keySet = bundle.keySet(); - String[] keys = keySet.toArray(new String[0]); - // Sort keys to make output deterministic. We need a custom comparator to handle + String[] names = bundle.keySet().toArray(new String[0]); + // Sort names to make output deterministic. We need a custom comparator to handle // nulls (arbitrarily putting them first, similar to Comparator.nullsFirst, which is // only available since N). Arrays.sort( - keys, + names, (@Nullable String s1, @Nullable String s2) -> { if (s1 == null) { return s2 == null ? 0 : -1; @@ -520,9 +887,9 @@ public class GenericDocument { return s1.compareTo(s2); } }); - for (String key : keys) { - stringBuilder.append("{ key: '").append(key).append("' value: "); - Object valueObject = bundle.get(key); + for (String name : names) { + stringBuilder.append("{ name: '").append(name).append("' value: "); + Object valueObject = bundle.get(name); if (valueObject == null) { stringBuilder.append("<null>"); } else if (valueObject instanceof Bundle) { @@ -540,9 +907,11 @@ public class GenericDocument { stringBuilder.append("' "); } stringBuilder.append("]"); - } else if (valueObject instanceof ArrayList) { - for (Bundle innerBundle : (ArrayList<Bundle>) valueObject) { - stringBuilder.append(bundleToString(innerBundle)); + } else if (valueObject instanceof List) { + @SuppressWarnings("unchecked") + List<Bundle> bundles = (List<Bundle>) valueObject; + for (int i = 0; i < bundles.size(); i++) { + stringBuilder.append(bundleToString(bundles.get(i))); } } else { stringBuilder.append(valueObject.toString()); @@ -577,12 +946,12 @@ public class GenericDocument { * * <p>Once {@link #build} is called, the instance can no longer be used. * - * <p>URIs are unique within a namespace. + * <p>Document IDs are unique within a namespace. * * <p>The number of namespaces per app should be kept small for efficiency reasons. * * @param namespace the namespace to set for the {@link GenericDocument}. - * @param uri the URI to set for the {@link GenericDocument}. + * @param id the unique identifier for the {@link GenericDocument} in its namespace. * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The * provided {@code schemaType} must be defined using {@link AppSearchSession#setSchema} * prior to inserting a document of this {@code schemaType} into the AppSearch index @@ -591,13 +960,13 @@ public class GenericDocument { * AppSearchResult#RESULT_NOT_FOUND}. */ @SuppressWarnings("unchecked") - public Builder(@NonNull String namespace, @NonNull String uri, @NonNull String schemaType) { + public Builder(@NonNull String namespace, @NonNull String id, @NonNull String schemaType) { Objects.requireNonNull(namespace); - Objects.requireNonNull(uri); + Objects.requireNonNull(id); Objects.requireNonNull(schemaType); mBuilderTypeInstance = (BuilderType) this; mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace); - mBundle.putString(GenericDocument.URI_FIELD, uri); + mBundle.putString(GenericDocument.ID_FIELD, id); mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType); // Set current timestamp for creation timestamp by default. mBundle.putLong( @@ -641,7 +1010,8 @@ public class GenericDocument { * @throws IllegalStateException if the builder has already been used. */ @NonNull - public BuilderType setCreationTimestampMillis(long creationTimestampMillis) { + public BuilderType setCreationTimestampMillis( + @CurrentTimeMillisLong long creationTimestampMillis) { Preconditions.checkState(!mBuilt, "Builder has already been used"); mBundle.putLong( GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, creationTimestampMillis); @@ -674,18 +1044,19 @@ public class GenericDocument { /** * Sets one or multiple {@code String} values for a property, replacing its previous values. * - * @param key the key associated with the {@code values}. + * @param name the name associated with the {@code values}. Must match the name for this + * property as given in {@link AppSearchSchema.PropertyConfig#getName}. * @param values the {@code String} values of the property. * @throws IllegalArgumentException if no values are provided, if provided values exceed * maximum repeated property length, or if a passed in {@code String} is {@code null}. * @throws IllegalStateException if the builder has already been used. */ @NonNull - public BuilderType setPropertyString(@NonNull String key, @NonNull String... values) { + public BuilderType setPropertyString(@NonNull String name, @NonNull String... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Objects.requireNonNull(key); + Objects.requireNonNull(name); Objects.requireNonNull(values); - putInPropertyBundle(key, values); + putInPropertyBundle(name, values); return mBuilderTypeInstance; } @@ -693,69 +1064,73 @@ public class GenericDocument { * Sets one or multiple {@code boolean} values for a property, replacing its previous * values. * - * @param key the key associated with the {@code values}. + * @param name the name associated with the {@code values}. Must match the name for this + * property as given in {@link AppSearchSchema.PropertyConfig#getName}. * @param values the {@code boolean} values of the property. * @throws IllegalArgumentException if values exceed maximum repeated property length. * @throws IllegalStateException if the builder has already been used. */ @NonNull - public BuilderType setPropertyBoolean(@NonNull String key, @NonNull boolean... values) { + public BuilderType setPropertyBoolean(@NonNull String name, @NonNull boolean... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Objects.requireNonNull(key); + Objects.requireNonNull(name); Objects.requireNonNull(values); - putInPropertyBundle(key, values); + putInPropertyBundle(name, values); return mBuilderTypeInstance; } /** * Sets one or multiple {@code long} values for a property, replacing its previous values. * - * @param key the key associated with the {@code values}. + * @param name the name associated with the {@code values}. Must match the name for this + * property as given in {@link AppSearchSchema.PropertyConfig#getName}. * @param values the {@code long} values of the property. * @throws IllegalArgumentException if values exceed maximum repeated property length. * @throws IllegalStateException if the builder has already been used. */ @NonNull - public BuilderType setPropertyLong(@NonNull String key, @NonNull long... values) { + public BuilderType setPropertyLong(@NonNull String name, @NonNull long... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Objects.requireNonNull(key); + Objects.requireNonNull(name); Objects.requireNonNull(values); - putInPropertyBundle(key, values); + putInPropertyBundle(name, values); return mBuilderTypeInstance; } /** * Sets one or multiple {@code double} values for a property, replacing its previous values. * - * @param key the key associated with the {@code values}. + * @param name the name associated with the {@code values}. Must match the name for this + * property as given in {@link AppSearchSchema.PropertyConfig#getName}. * @param values the {@code double} values of the property. * @throws IllegalArgumentException if values exceed maximum repeated property length. * @throws IllegalStateException if the builder has already been used. */ @NonNull - public BuilderType setPropertyDouble(@NonNull String key, @NonNull double... values) { + public BuilderType setPropertyDouble(@NonNull String name, @NonNull double... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Objects.requireNonNull(key); + Objects.requireNonNull(name); Objects.requireNonNull(values); - putInPropertyBundle(key, values); + putInPropertyBundle(name, values); return mBuilderTypeInstance; } /** * Sets one or multiple {@code byte[]} for a property, replacing its previous values. * - * @param key the key associated with the {@code values}. + * @param name the name associated with the {@code values}. Must match the name for this + * property as given in {@link AppSearchSchema.PropertyConfig#getName}. * @param values the {@code byte[]} of the property. * @throws IllegalArgumentException if no values are provided, if provided values exceed * maximum repeated property length, or if a passed in {@code byte[]} is {@code null}. * @throws IllegalStateException if the builder has already been used. */ @NonNull - public BuilderType setPropertyBytes(@NonNull String key, @NonNull byte[]... values) { + public BuilderType setPropertyBytes(@NonNull String name, @NonNull byte[]... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Objects.requireNonNull(key); + Objects.requireNonNull(name); Objects.requireNonNull(values); - putInPropertyBundle(key, values); + putInPropertyBundle(name, values); return mBuilderTypeInstance; } @@ -763,7 +1138,8 @@ public class GenericDocument { * Sets one or multiple {@link GenericDocument} values for a property, replacing its * previous values. * - * @param key the key associated with the {@code values}. + * @param name the name associated with the {@code values}. Must match the name for this + * property as given in {@link AppSearchSchema.PropertyConfig#getName}. * @param values the {@link GenericDocument} values of the property. * @throws IllegalArgumentException if no values are provided, if provided values exceed if * provided values exceed maximum repeated property length, or if a passed in {@link @@ -772,17 +1148,17 @@ public class GenericDocument { */ @NonNull public BuilderType setPropertyDocument( - @NonNull String key, @NonNull GenericDocument... values) { + @NonNull String name, @NonNull GenericDocument... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Objects.requireNonNull(key); + Objects.requireNonNull(name); Objects.requireNonNull(values); - putInPropertyBundle(key, values); + putInPropertyBundle(name, values); return mBuilderTypeInstance; } - private void putInPropertyBundle(@NonNull String key, @NonNull String[] values) + private void putInPropertyBundle(@NonNull String name, @NonNull String[] values) throws IllegalArgumentException { - validateRepeatedPropertyLength(key, values.length); + validateRepeatedPropertyLength(name, values.length); for (int i = 0; i < values.length; i++) { if (values[i] == null) { throw new IllegalArgumentException("The String at " + i + " is null."); @@ -797,22 +1173,22 @@ public class GenericDocument { + "."); } } - mProperties.putStringArray(key, values); + mProperties.putStringArray(name, values); } - private void putInPropertyBundle(@NonNull String key, @NonNull boolean[] values) { - validateRepeatedPropertyLength(key, values.length); - mProperties.putBooleanArray(key, values); + private void putInPropertyBundle(@NonNull String name, @NonNull boolean[] values) { + validateRepeatedPropertyLength(name, values.length); + mProperties.putBooleanArray(name, values); } - private void putInPropertyBundle(@NonNull String key, @NonNull double[] values) { - validateRepeatedPropertyLength(key, values.length); - mProperties.putDoubleArray(key, values); + private void putInPropertyBundle(@NonNull String name, @NonNull double[] values) { + validateRepeatedPropertyLength(name, values.length); + mProperties.putDoubleArray(name, values); } - private void putInPropertyBundle(@NonNull String key, @NonNull long[] values) { - validateRepeatedPropertyLength(key, values.length); - mProperties.putLongArray(key, values); + private void putInPropertyBundle(@NonNull String name, @NonNull long[] values) { + validateRepeatedPropertyLength(name, values.length); + mProperties.putLongArray(name, values); } /** @@ -821,8 +1197,8 @@ public class GenericDocument { * <p>Bundle doesn't support for two dimension array byte[][], we are converting byte[][] * into ArrayList<Bundle>, and each elements will contain a one dimension byte[]. */ - private void putInPropertyBundle(@NonNull String key, @NonNull byte[][] values) { - validateRepeatedPropertyLength(key, values.length); + private void putInPropertyBundle(@NonNull String name, @NonNull byte[][] values) { + validateRepeatedPropertyLength(name, values.length); ArrayList<Bundle> bundles = new ArrayList<>(values.length); for (int i = 0; i < values.length; i++) { if (values[i] == null) { @@ -832,11 +1208,11 @@ public class GenericDocument { bundle.putByteArray(BYTE_ARRAY_FIELD, values[i]); bundles.add(bundle); } - mProperties.putParcelableArrayList(key, bundles); + mProperties.putParcelableArrayList(name, bundles); } - private void putInPropertyBundle(@NonNull String key, @NonNull GenericDocument[] values) { - validateRepeatedPropertyLength(key, values.length); + private void putInPropertyBundle(@NonNull String name, @NonNull GenericDocument[] values) { + validateRepeatedPropertyLength(name, values.length); Parcelable[] documentBundles = new Parcelable[values.length]; for (int i = 0; i < values.length; i++) { if (values[i] == null) { @@ -844,14 +1220,14 @@ public class GenericDocument { } documentBundles[i] = values[i].mBundle; } - mProperties.putParcelableArray(key, documentBundles); + mProperties.putParcelableArray(name, documentBundles); } - private static void validateRepeatedPropertyLength(@NonNull String key, int length) { + private static void validateRepeatedPropertyLength(@NonNull String name, int length) { if (length > MAX_REPEATED_PROPERTY_LENGTH) { throw new IllegalArgumentException( "Repeated property \"" - + key + + name + "\" has length " + length + ", which exceeds the limit of " diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java index 4dc3225bd179..58fd8bee8d4a 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java @@ -32,29 +32,29 @@ import java.util.Objects; import java.util.Set; /** - * Encapsulates a request to retrieve documents by namespace and URIs from the {@link + * Encapsulates a request to retrieve documents by namespace and IDs from the {@link * AppSearchSession} database. * - * @see AppSearchSession#getByUri + * @see AppSearchSession#getByDocumentId */ -public final class GetByUriRequest { +public final class GetByDocumentIdRequest { /** - * Schema type to be used in {@link android.app.appsearch.GetByUriRequest.Builder#addProjection} - * to apply property paths to all results, excepting any types that have had their own, specific - * property paths set. + * Schema type to be used in {@link GetByDocumentIdRequest.Builder#addProjection} to apply + * property paths to all results, excepting any types that have had their own, specific property + * paths set. */ public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*"; private final String mNamespace; - private final Set<String> mUris; + private final Set<String> mIds; private final Map<String, List<String>> mTypePropertyPathsMap; - GetByUriRequest( + GetByDocumentIdRequest( @NonNull String namespace, - @NonNull Set<String> uris, + @NonNull Set<String> ids, @NonNull Map<String, List<String>> typePropertyPathsMap) { mNamespace = Objects.requireNonNull(namespace); - mUris = Objects.requireNonNull(uris); + mIds = Objects.requireNonNull(ids); mTypePropertyPathsMap = Objects.requireNonNull(typePropertyPathsMap); } @@ -64,10 +64,10 @@ public final class GetByUriRequest { return mNamespace; } - /** Returns the set of URIs attached to the request. */ + /** Returns the set of document IDs attached to the request. */ @NonNull - public Set<String> getUris() { - return Collections.unmodifiableSet(mUris); + public Set<String> getIds() { + return Collections.unmodifiableSet(mIds); } /** @@ -81,8 +81,8 @@ public final class GetByUriRequest { @NonNull public Map<String, List<String>> getProjections() { Map<String, List<String>> copy = new ArrayMap<>(); - for (String key : mTypePropertyPathsMap.keySet()) { - copy.put(key, new ArrayList<>(mTypePropertyPathsMap.get(key))); + for (Map.Entry<String, List<String>> entry : mTypePropertyPathsMap.entrySet()) { + copy.put(entry.getKey(), new ArrayList<>(entry.getValue())); } return copy; } @@ -103,42 +103,42 @@ public final class GetByUriRequest { } /** - * Builder for {@link GetByUriRequest} objects. + * Builder for {@link GetByDocumentIdRequest} objects. * * <p>Once {@link #build} is called, the instance can no longer be used. */ public static final class Builder { private final String mNamespace; - private final Set<String> mUris = new ArraySet<>(); + private final Set<String> mIds = new ArraySet<>(); private final Map<String, List<String>> mProjectionTypePropertyPaths = new ArrayMap<>(); private boolean mBuilt = false; - /** Creates a {@link GetByUriRequest.Builder} instance. */ + /** Creates a {@link GetByDocumentIdRequest.Builder} instance. */ public Builder(@NonNull String namespace) { mNamespace = Objects.requireNonNull(namespace); } /** - * Adds one or more URIs to the request. + * Adds one or more document IDs to the request. * * @throws IllegalStateException if the builder has already been used. */ @NonNull - public Builder addUris(@NonNull String... uris) { - Objects.requireNonNull(uris); - return addUris(Arrays.asList(uris)); + public Builder addIds(@NonNull String... ids) { + Objects.requireNonNull(ids); + return addIds(Arrays.asList(ids)); } /** - * Adds a collection of URIs to the request. + * Adds a collection of IDs to the request. * * @throws IllegalStateException if the builder has already been used. */ @NonNull - public Builder addUris(@NonNull Collection<String> uris) { + public Builder addIds(@NonNull Collection<String> ids) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Objects.requireNonNull(uris); - mUris.addAll(uris); + Objects.requireNonNull(ids); + mIds.addAll(ids); return this; } @@ -152,8 +152,9 @@ public final class GetByUriRequest { * of that type will be retrieved. * * <p>If property path is added for the {@link - * GetByUriRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will apply to - * all results, excepting any types that have their own, specific property paths set. + * GetByDocumentIdRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will + * apply to all results, excepting any types that have their own, specific property paths + * set. * * @throws IllegalStateException if the builder has already been used. * @see SearchSpec.Builder#addProjection @@ -174,15 +175,15 @@ public final class GetByUriRequest { } /** - * Builds a new {@link GetByUriRequest}. + * Builds a new {@link GetByDocumentIdRequest}. * * @throws IllegalStateException if the builder has already been used. */ @NonNull - public GetByUriRequest build() { + public GetByDocumentIdRequest build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); mBuilt = true; - return new GetByUriRequest(mNamespace, mUris, mProjectionTypePropertyPaths); + return new GetByDocumentIdRequest(mNamespace, mIds, mProjectionTypePropertyPaths); } } } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java index 691ef4ff939f..6f8cbe84ab50 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java @@ -75,7 +75,7 @@ public class GetSchemaResponse { /** Builder for {@link GetSchemaResponse} objects. */ public static final class Builder { - private int mVersion; + private int mVersion = 0; private boolean mBuilt = false; private final ArrayList<Bundle> mSchemaBundles = new ArrayList<>(); diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/Migrator.java b/apex/appsearch/framework/java/external/android/app/appsearch/Migrator.java index 511b42ac8f12..c5a0f63c94b6 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/Migrator.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/Migrator.java @@ -52,7 +52,7 @@ public abstract class Migrator { * * <p>If this {@link Migrator} is provided to cover a compatible schema change via {@link * AppSearchSession#setSchema}, documents under the old version won't be removed unless you use - * the same URI. + * the same document ID. * * <p>This method will be invoked on the background worker thread provided via {@link * AppSearchSession#setSchema}. @@ -75,7 +75,7 @@ public abstract class Migrator { * * <p>If this {@link Migrator} is provided to cover a compatible schema change via {@link * AppSearchSession#setSchema}, documents under the old version won't be removed unless you use - * the same URI. + * the same document ID. * * <p>This method will be invoked on the background worker thread. * diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByDocumentIdRequest.java index 4dcad68d49be..1afbe27116ca 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByDocumentIdRequest.java @@ -28,18 +28,18 @@ import java.util.Objects; import java.util.Set; /** - * Encapsulates a request to remove documents by namespace and URIs from the {@link - * AppSearchSession} database. + * Encapsulates a request to remove documents by namespace and IDs from the {@link AppSearchSession} + * database. * * @see AppSearchSession#remove */ -public final class RemoveByUriRequest { +public final class RemoveByDocumentIdRequest { private final String mNamespace; - private final Set<String> mUris; + private final Set<String> mIds; - RemoveByUriRequest(String namespace, Set<String> uris) { + RemoveByDocumentIdRequest(String namespace, Set<String> ids) { mNamespace = namespace; - mUris = uris; + mIds = ids; } /** Returns the namespace to remove documents from. */ @@ -48,61 +48,61 @@ public final class RemoveByUriRequest { return mNamespace; } - /** Returns the set of URIs attached to the request. */ + /** Returns the set of document IDs attached to the request. */ @NonNull - public Set<String> getUris() { - return Collections.unmodifiableSet(mUris); + public Set<String> getIds() { + return Collections.unmodifiableSet(mIds); } /** - * Builder for {@link RemoveByUriRequest} objects. + * Builder for {@link RemoveByDocumentIdRequest} objects. * * <p>Once {@link #build} is called, the instance can no longer be used. */ public static final class Builder { private final String mNamespace; - private final Set<String> mUris = new ArraySet<>(); + private final Set<String> mIds = new ArraySet<>(); private boolean mBuilt = false; - /** Creates a {@link RemoveByUriRequest.Builder} instance. */ + /** Creates a {@link RemoveByDocumentIdRequest.Builder} instance. */ public Builder(@NonNull String namespace) { mNamespace = Objects.requireNonNull(namespace); } /** - * Adds one or more URIs to the request. + * Adds one or more document IDs to the request. * * @throws IllegalStateException if the builder has already been used. */ @NonNull - public Builder addUris(@NonNull String... uris) { - Objects.requireNonNull(uris); - return addUris(Arrays.asList(uris)); + public Builder addIds(@NonNull String... ids) { + Objects.requireNonNull(ids); + return addIds(Arrays.asList(ids)); } /** - * Adds a collection of URIs to the request. + * Adds a collection of IDs to the request. * * @throws IllegalStateException if the builder has already been used. */ @NonNull - public Builder addUris(@NonNull Collection<String> uris) { + public Builder addIds(@NonNull Collection<String> ids) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Objects.requireNonNull(uris); - mUris.addAll(uris); + Objects.requireNonNull(ids); + mIds.addAll(ids); return this; } /** - * Builds a new {@link RemoveByUriRequest}. + * Builds a new {@link RemoveByDocumentIdRequest}. * * @throws IllegalStateException if the builder has already been used. */ @NonNull - public RemoveByUriRequest build() { + public RemoveByDocumentIdRequest build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); mBuilt = true; - return new RemoveByUriRequest(mNamespace, mUris); + return new RemoveByDocumentIdRequest(mNamespace, mIds); } } } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java index 8aff3b41cbc7..3947ba78cdd5 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java @@ -16,6 +16,7 @@ package android.app.appsearch; +import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; import com.android.internal.util.Preconditions; @@ -35,20 +36,20 @@ public final class ReportSystemUsageRequest { private final String mPackageName; private final String mDatabase; private final String mNamespace; - private final String mUri; - private final long mUsageTimeMillis; + private final String mDocumentId; + private final long mUsageTimestampMillis; ReportSystemUsageRequest( @NonNull String packageName, @NonNull String database, @NonNull String namespace, - @NonNull String uri, - long usageTimeMillis) { + @NonNull String documentId, + long usageTimestampMillis) { mPackageName = Objects.requireNonNull(packageName); mDatabase = Objects.requireNonNull(database); mNamespace = Objects.requireNonNull(namespace); - mUri = Objects.requireNonNull(uri); - mUsageTimeMillis = usageTimeMillis; + mDocumentId = Objects.requireNonNull(documentId); + mUsageTimestampMillis = usageTimestampMillis; } /** Returns the package name of the app which owns the document that was used. */ @@ -69,10 +70,10 @@ public final class ReportSystemUsageRequest { return mNamespace; } - /** Returns the URI of document that was used. */ + /** Returns the ID of document that was used. */ @NonNull - public String getUri() { - return mUri; + public String getDocumentId() { + return mDocumentId; } /** @@ -81,8 +82,9 @@ public final class ReportSystemUsageRequest { * * <p>The value is in the {@link System#currentTimeMillis} time base. */ - public long getUsageTimeMillis() { - return mUsageTimeMillis; + @CurrentTimeMillisLong + public long getUsageTimestampMillis() { + return mUsageTimestampMillis; } /** Builder for {@link ReportSystemUsageRequest} objects. */ @@ -90,31 +92,20 @@ public final class ReportSystemUsageRequest { private final String mPackageName; private final String mDatabase; private final String mNamespace; - private String mUri; - private Long mUsageTimeMillis; + private final String mDocumentId; + private Long mUsageTimestampMillis; private boolean mBuilt = false; /** Creates a {@link ReportSystemUsageRequest.Builder} instance. */ public Builder( - @NonNull String packageName, @NonNull String database, @NonNull String namespace) { + @NonNull String packageName, + @NonNull String database, + @NonNull String namespace, + @NonNull String documentId) { mPackageName = Objects.requireNonNull(packageName); mDatabase = Objects.requireNonNull(database); mNamespace = Objects.requireNonNull(namespace); - } - - /** - * Sets the URI of the document being used. - * - * <p>This field is required. - * - * @throws IllegalStateException if the builder has already been used - */ - @NonNull - public ReportSystemUsageRequest.Builder setUri(@NonNull String uri) { - Preconditions.checkState(!mBuilt, "Builder has already been used"); - Objects.requireNonNull(uri); - mUri = uri; - return this; + mDocumentId = Objects.requireNonNull(documentId); } /** @@ -129,28 +120,27 @@ public final class ReportSystemUsageRequest { * @throws IllegalStateException if the builder has already been used */ @NonNull - public ReportSystemUsageRequest.Builder setUsageTimeMillis(long usageTimeMillis) { + public ReportSystemUsageRequest.Builder setUsageTimestampMillis( + @CurrentTimeMillisLong long usageTimestampMillis) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mUsageTimeMillis = usageTimeMillis; + mUsageTimestampMillis = usageTimestampMillis; return this; } /** * Builds a new {@link ReportSystemUsageRequest}. * - * @throws NullPointerException if {@link #setUri} has never been called * @throws IllegalStateException if the builder has already been used */ @NonNull public ReportSystemUsageRequest build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Objects.requireNonNull(mUri, "ReportUsageRequest is missing a URI"); - if (mUsageTimeMillis == null) { - mUsageTimeMillis = System.currentTimeMillis(); + if (mUsageTimestampMillis == null) { + mUsageTimestampMillis = System.currentTimeMillis(); } mBuilt = true; return new ReportSystemUsageRequest( - mPackageName, mDatabase, mNamespace, mUri, mUsageTimeMillis); + mPackageName, mDatabase, mNamespace, mDocumentId, mUsageTimestampMillis); } } } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java index 925bde92d69d..8c8ade8c52d0 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java @@ -16,6 +16,7 @@ package android.app.appsearch; +import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; import com.android.internal.util.Preconditions; @@ -31,13 +32,14 @@ import java.util.Objects; */ public final class ReportUsageRequest { private final String mNamespace; - private final String mUri; - private final long mUsageTimeMillis; + private final String mDocumentId; + private final long mUsageTimestampMillis; - ReportUsageRequest(@NonNull String namespace, @NonNull String uri, long usageTimeMillis) { + ReportUsageRequest( + @NonNull String namespace, @NonNull String documentId, long usageTimestampMillis) { mNamespace = Objects.requireNonNull(namespace); - mUri = Objects.requireNonNull(uri); - mUsageTimeMillis = usageTimeMillis; + mDocumentId = Objects.requireNonNull(documentId); + mUsageTimestampMillis = usageTimestampMillis; } /** Returns the namespace of the document that was used. */ @@ -46,10 +48,10 @@ public final class ReportUsageRequest { return mNamespace; } - /** Returns the URI of document that was used. */ + /** Returns the ID of document that was used. */ @NonNull - public String getUri() { - return mUri; + public String getDocumentId() { + return mDocumentId; } /** @@ -58,35 +60,22 @@ public final class ReportUsageRequest { * * <p>The value is in the {@link System#currentTimeMillis} time base. */ - public long getUsageTimeMillis() { - return mUsageTimeMillis; + @CurrentTimeMillisLong + public long getUsageTimestampMillis() { + return mUsageTimestampMillis; } /** Builder for {@link ReportUsageRequest} objects. */ public static final class Builder { private final String mNamespace; - private String mUri; - private Long mUsageTimeMillis; + private final String mDocumentId; + private Long mUsageTimestampMillis; private boolean mBuilt = false; /** Creates a {@link ReportUsageRequest.Builder} instance. */ - public Builder(@NonNull String namespace) { + public Builder(@NonNull String namespace, @NonNull String documentId) { mNamespace = Objects.requireNonNull(namespace); - } - - /** - * 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"); - Objects.requireNonNull(uri); - mUri = uri; - return this; + mDocumentId = Objects.requireNonNull(documentId); } /** @@ -101,27 +90,26 @@ public final class ReportUsageRequest { * @throws IllegalStateException if the builder has already been used */ @NonNull - public ReportUsageRequest.Builder setUsageTimeMillis(long usageTimeMillis) { + public ReportUsageRequest.Builder setUsageTimestampMillis( + @CurrentTimeMillisLong long usageTimestampMillis) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mUsageTimeMillis = usageTimeMillis; + mUsageTimestampMillis = usageTimestampMillis; 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"); - Objects.requireNonNull(mUri, "ReportUsageRequest is missing a URI"); - if (mUsageTimeMillis == null) { - mUsageTimeMillis = System.currentTimeMillis(); + if (mUsageTimestampMillis == null) { + mUsageTimestampMillis = System.currentTimeMillis(); } mBuilt = true; - return new ReportUsageRequest(mNamespace, mUri, mUsageTimeMillis); + return new ReportUsageRequest(mNamespace, mDocumentId, mUsageTimestampMillis); } } } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java index 432f838bc78c..4fc654f7e5d2 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java @@ -415,16 +415,13 @@ public final class SearchResult { private static String getPropertyValues(GenericDocument document, String propertyName) { // In IcingLib snippeting is available for only 3 data types i.e String, double and // long, so we need to check which of these three are requested. - // TODO (tytytyww): getPropertyStringArray takes property name, handle for property - // path. // TODO (tytytyww): support double[] and long[]. - String[] values = document.getPropertyStringArray(propertyName); - if (values == null) { - throw new IllegalStateException("No content found for requested property path!"); + String result = document.getPropertyString(propertyName); + if (result == null) { + throw new IllegalStateException( + "No content found for requested property path: " + propertyName); } - - // TODO(b/175146044): Return the proper match based on the index in the propertyName. - return values[0]; + return result; } /** Builder for {@link MatchInfo} objects. */ @@ -433,23 +430,22 @@ public final class SearchResult { private boolean mBuilt = false; /** - * Sets the property path corresponding to the given entry. + * Creates a new {@link MatchInfo.Builder} reporting a match with the given property + * path. * - * <p>A property path is a '.' - delimited sequence of property names indicating which + * <p>A property path is a dot-delimited sequence of property names indicating which * property in the document these snippets correspond to. * - * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class - * example 1 this returns "subject" + * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. + * For class example 1 this returns "subject". * - * @throws IllegalStateException if the builder has already been used + * @param propertyPath A {@code dot-delimited sequence of property names indicating + * which property in the document these snippets correspond to. */ - @NonNull - public Builder setPropertyPath(@NonNull String propertyPath) { - Preconditions.checkState(!mBuilt, "Builder has already been used"); + public Builder(@NonNull String propertyPath) { mBundle.putString( SearchResult.MatchInfo.PROPERTY_PATH_FIELD, Objects.requireNonNull(propertyPath)); - return this; } /** diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java index d466bf1359d0..20e5b9da8d53 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SuppressLint; -import android.app.appsearch.exceptions.IllegalSearchSpecException; import android.os.Bundle; import android.util.ArrayMap; @@ -323,9 +322,15 @@ public final class SearchSpec { public Builder() { mBundle = new Bundle(); mBundle.putInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE); + mBundle.putInt(TERM_MATCH_TYPE_FIELD, TERM_MATCH_PREFIX); } - /** Indicates how the query terms should match {@code TermMatchCode} in the index. */ + /** + * Indicates how the query terms should match {@code TermMatchCode} in the index. + * + * <p>If this method is not called, the default term match type is {@link + * SearchSpec#TERM_MATCH_PREFIX}. + */ @NonNull public Builder setTermMatch(@TermMatch int termMatchTypeCode) { Preconditions.checkState(!mBuilt, "Builder has already been used"); @@ -634,9 +639,6 @@ public final class SearchSpec { @NonNull public SearchSpec build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); - if (!mBundle.containsKey(TERM_MATCH_TYPE_FIELD)) { - throw new IllegalSearchSpecException("Missing termMatchType field."); - } mBundle.putStringArrayList(NAMESPACE_FIELD, mNamespaces); mBundle.putStringArrayList(SCHEMA_FIELD, mSchemas); mBundle.putStringArrayList(PACKAGE_NAME_FIELD, mPackageNames); diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java index 8f7a0bf61f3e..275b2c337c3c 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java @@ -414,7 +414,6 @@ public final class SetSchemaRequest { @NonNull public SetSchemaRequest build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mBuilt = true; // Verify that any schema types with display or visibility settings refer to a real // schema. @@ -432,6 +431,7 @@ public final class SetSchemaRequest { "Schema types " + referencedSchemas + " referenced, but were not added."); } + mBuilt = true; return new SetSchemaRequest( mSchemas, mSchemasNotDisplayedBySystem, diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java index 7be589f727ce..8a162d49e151 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java @@ -86,7 +86,7 @@ public class SetSchemaResponse { * <p>A {@link MigrationFailure} will be generated if the system trying to save a post-migrated * {@link GenericDocument} but fail. * - * <p>{@link MigrationFailure} contains the uri, namespace and schemaType of the post-migrated + * <p>{@link MigrationFailure} contains the namespace, id and schemaType of the post-migrated * {@link GenericDocument} and the error reason. Mostly it will be mismatch the schema it * migrated to. */ @@ -257,14 +257,40 @@ public class SetSchemaResponse { public static class MigrationFailure { private static final String SCHEMA_TYPE_FIELD = "schemaType"; private static final String NAMESPACE_FIELD = "namespace"; - private static final String URI_FIELD = "uri"; + private static final String DOCUMENT_ID_FIELD = "id"; private static final String ERROR_MESSAGE_FIELD = "errorMessage"; private static final String RESULT_CODE_FIELD = "resultCode"; private final Bundle mBundle; + /** + * Constructs a new {@link MigrationFailure}. + * + * @param namespace The namespace of the document which failed to be migrated. + * @param documentId The id of the document which failed to be migrated. + * @param schemaType The type of the document which failed to be migrated. + * @param failedResult The reason why the document failed to be indexed. + * @throws IllegalArgumentException if the provided {@code failedResult} was not a failure. + */ + public MigrationFailure( + @NonNull String namespace, + @NonNull String documentId, + @NonNull String schemaType, + @NonNull AppSearchResult<?> failedResult) { + mBundle = new Bundle(); + mBundle.putString(NAMESPACE_FIELD, Objects.requireNonNull(namespace)); + mBundle.putString(DOCUMENT_ID_FIELD, Objects.requireNonNull(documentId)); + mBundle.putString(SCHEMA_TYPE_FIELD, Objects.requireNonNull(schemaType)); + + Objects.requireNonNull(failedResult); + Preconditions.checkArgument( + !failedResult.isSuccess(), "failedResult was actually successful"); + mBundle.putString(ERROR_MESSAGE_FIELD, failedResult.getErrorMessage()); + mBundle.putInt(RESULT_CODE_FIELD, failedResult.getResultCode()); + } + MigrationFailure(@NonNull Bundle bundle) { - mBundle = bundle; + mBundle = Objects.requireNonNull(bundle); } /** @@ -277,27 +303,27 @@ public class SetSchemaResponse { return mBundle; } - /** Returns the schema type of the {@link GenericDocument} that fails to be migrated. */ + /** Returns the namespace of the {@link GenericDocument} that failed to be migrated. */ @NonNull - public String getSchemaType() { - return mBundle.getString(SCHEMA_TYPE_FIELD, /*defaultValue=*/ ""); + public String getNamespace() { + return mBundle.getString(NAMESPACE_FIELD, /*defaultValue=*/ ""); } - /** Returns the namespace of the {@link GenericDocument} that fails to be migrated. */ + /** Returns the id of the {@link GenericDocument} that failed to be migrated. */ @NonNull - public String getNamespace() { - return mBundle.getString(NAMESPACE_FIELD, /*defaultValue=*/ ""); + public String getDocumentId() { + return mBundle.getString(DOCUMENT_ID_FIELD, /*defaultValue=*/ ""); } - /** Returns the uri of the {@link GenericDocument} that fails to be migrated. */ + /** Returns the schema type of the {@link GenericDocument} that failed to be migrated. */ @NonNull - public String getUri() { - return mBundle.getString(URI_FIELD, /*defaultValue=*/ ""); + public String getSchemaType() { + return mBundle.getString(SCHEMA_TYPE_FIELD, /*defaultValue=*/ ""); } /** - * Returns the {@link AppSearchResult} that indicates why the post-migrated {@link - * GenericDocument} fails to be saved. + * Returns the {@link AppSearchResult} that indicates why the post-migration {@link + * GenericDocument} failed to be indexed. */ @NonNull public AppSearchResult<Void> getAppSearchResult() { @@ -305,61 +331,5 @@ public class SetSchemaResponse { mBundle.getInt(RESULT_CODE_FIELD), mBundle.getString(ERROR_MESSAGE_FIELD, /*defaultValue=*/ "")); } - - /** Builder for {@link MigrationFailure} objects. */ - public static final class Builder { - private String mSchemaType; - private String mNamespace; - private String mUri; - private final Bundle mBundle = new Bundle(); - private AppSearchResult<Void> mFailureResult; - private boolean mBuilt = false; - - /** Sets the schema type for the {@link MigrationFailure}. */ - @NonNull - public Builder setSchemaType(@NonNull String schemaType) { - Preconditions.checkState(!mBuilt, "Builder has already been used"); - mSchemaType = Objects.requireNonNull(schemaType); - return this; - } - - /** Sets the namespace for the {@link MigrationFailure}. */ - @NonNull - public Builder setNamespace(@NonNull String namespace) { - Preconditions.checkState(!mBuilt, "Builder has already been used"); - mNamespace = Objects.requireNonNull(namespace); - return this; - } - - /** Sets the uri for the {@link MigrationFailure}. */ - @NonNull - public Builder setUri(@NonNull String uri) { - Preconditions.checkState(!mBuilt, "Builder has already been used"); - mUri = Objects.requireNonNull(uri); - return this; - } - - /** Sets the failure {@link AppSearchResult} for the {@link MigrationFailure}. */ - @NonNull - public Builder setAppSearchResult(@NonNull AppSearchResult<Void> appSearchResult) { - Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkState(!appSearchResult.isSuccess(), "Input a success result"); - mFailureResult = Objects.requireNonNull(appSearchResult); - return this; - } - - /** Builds a {@link MigrationFailure} object. */ - @NonNull - public MigrationFailure build() { - Preconditions.checkState(!mBuilt, "Builder has already been used"); - mBundle.putString(SCHEMA_TYPE_FIELD, mSchemaType); - mBundle.putString(NAMESPACE_FIELD, mNamespace); - mBundle.putString(URI_FIELD, mUri); - mBundle.putString(ERROR_MESSAGE_FIELD, mFailureResult.getErrorMessage()); - mBundle.putInt(RESULT_CODE_FIELD, mFailureResult.getResultCode()); - mBuilt = true; - return new MigrationFailure(mBundle); - } - } } } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSearchSpecException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSearchSpecException.java deleted file mode 100644 index 0b5dc2ece6f1..000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSearchSpecException.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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. - * 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.exceptions; - -import android.annotation.NonNull; - -/** - * Indicates that a {@link android.app.appsearch.SearchResult} has logical inconsistencies such as - * unpopulated mandatory fields or illegal combinations of parameters. - * - * @hide - */ -public class IllegalSearchSpecException extends IllegalArgumentException { - /** - * Constructs a new {@link IllegalSearchSpecException}. - * - * @param message A developer-readable description of the issue with the bundle. - */ - public IllegalSearchSpecException(@NonNull String message) { - super(message); - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index f6f5c98856c7..ec81ed25025a 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -308,13 +308,13 @@ public class AppSearchManagerService extends SystemService { GenericDocument document = new GenericDocument(documentBundles.get(i)); try { impl.putDocument(packageName, databaseName, document, logger); - resultBuilder.setSuccess(document.getUri(), /*result=*/ null); + resultBuilder.setSuccess(document.getId(), /*result=*/ null); ++operationSuccessCount; } catch (Throwable t) { - resultBuilder.setResult(document.getUri(), + resultBuilder.setResult(document.getId(), throwableToFailedResult(t)); AppSearchResult<Void> result = throwableToFailedResult(t); - resultBuilder.setResult(document.getUri(), result); + resultBuilder.setResult(document.getId(), result); // for failures, we would just log the one for last failure statusCode = result.getResultCode(); ++operationFailureCount; @@ -354,14 +354,14 @@ public class AppSearchManagerService extends SystemService { @NonNull String packageName, @NonNull String databaseName, @NonNull String namespace, - @NonNull List<String> uris, + @NonNull List<String> ids, @NonNull Map<String, List<String>> typePropertyPaths, @UserIdInt int userId, @NonNull IAppSearchBatchResultCallback callback) { Objects.requireNonNull(packageName); Objects.requireNonNull(databaseName); Objects.requireNonNull(namespace); - Objects.requireNonNull(uris); + Objects.requireNonNull(ids); Objects.requireNonNull(callback); int callingUid = Binder.getCallingUid(); int callingUserId = handleIncomingUser(userId, callingUid); @@ -373,19 +373,19 @@ public class AppSearchManagerService extends SystemService { new AppSearchBatchResult.Builder<>(); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId); - for (int i = 0; i < uris.size(); i++) { - String uri = uris.get(i); + for (int i = 0; i < ids.size(); i++) { + String id = ids.get(i); try { GenericDocument document = impl.getDocument( packageName, databaseName, namespace, - uri, + id, typePropertyPaths); - resultBuilder.setSuccess(uri, document.getBundle()); + resultBuilder.setSuccess(id, document.getBundle()); } catch (Throwable t) { - resultBuilder.setResult(uri, throwableToFailedResult(t)); + resultBuilder.setResult(id, throwableToFailedResult(t)); } } invokeCallbackOnResult(callback, resultBuilder.build()); @@ -578,14 +578,12 @@ public class AppSearchManagerService extends SystemService { impl.putDocument(packageName, databaseName, document, /*logger=*/ null); } catch (Throwable t) { - migrationFailureBundles.add( - new SetSchemaResponse.MigrationFailure.Builder() - .setNamespace(document.getNamespace()) - .setSchemaType(document.getSchemaType()) - .setUri(document.getUri()) - .setAppSearchResult(AppSearchResult - .throwableToFailedResult(t)) - .build().getBundle()); + migrationFailureBundles.add(new SetSchemaResponse.MigrationFailure( + document.getNamespace(), + document.getId(), + document.getSchemaType(), + AppSearchResult.throwableToFailedResult(t)) + .getBundle()); } } } @@ -603,14 +601,14 @@ public class AppSearchManagerService extends SystemService { @NonNull String packageName, @NonNull String databaseName, @NonNull String namespace, - @NonNull String uri, + @NonNull String documentId, long usageTimeMillis, boolean systemUsage, @UserIdInt int userId, @NonNull IAppSearchResultCallback callback) { Objects.requireNonNull(databaseName); Objects.requireNonNull(namespace); - Objects.requireNonNull(uri); + Objects.requireNonNull(documentId); Objects.requireNonNull(callback); int callingUid = Binder.getCallingUid(); int callingUserId = handleIncomingUser(userId, callingUid); @@ -625,7 +623,7 @@ public class AppSearchManagerService extends SystemService { AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId); impl.reportUsage( - packageName, databaseName, namespace, uri, + packageName, databaseName, namespace, documentId, usageTimeMillis, systemUsage); invokeCallbackOnResult( callback, AppSearchResult.newSuccessfulResult(/*result=*/ null)); @@ -636,16 +634,16 @@ public class AppSearchManagerService extends SystemService { } @Override - public void removeByUri( + public void removeByDocumentId( @NonNull String packageName, @NonNull String databaseName, @NonNull String namespace, - @NonNull List<String> uris, + @NonNull List<String> ids, @UserIdInt int userId, @NonNull IAppSearchBatchResultCallback callback) { Objects.requireNonNull(packageName); Objects.requireNonNull(databaseName); - Objects.requireNonNull(uris); + Objects.requireNonNull(ids); Objects.requireNonNull(callback); int callingUid = Binder.getCallingUid(); int callingUserId = handleIncomingUser(userId, callingUid); @@ -657,13 +655,13 @@ public class AppSearchManagerService extends SystemService { new AppSearchBatchResult.Builder<>(); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId); - for (int i = 0; i < uris.size(); i++) { - String uri = uris.get(i); + for (int i = 0; i < ids.size(); i++) { + String id = ids.get(i); try { - impl.remove(packageName, databaseName, namespace, uri); - resultBuilder.setSuccess(uri, /*result= */ null); + impl.remove(packageName, databaseName, namespace, id); + resultBuilder.setSuccess(id, /*result= */ null); } catch (Throwable t) { - resultBuilder.setResult(uri, throwableToFailedResult(t)); + resultBuilder.setResult(id, throwableToFailedResult(t)); } } // Now that the batch has been written. Persist the newly written data. diff --git a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java index 4de52fb65d93..a8d429b24c63 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java @@ -103,14 +103,13 @@ public class VisibilityStore { new AppSearchSchema.Builder(VISIBILITY_TYPE) .addProperty( new AppSearchSchema.StringPropertyConfig.Builder( - NOT_PLATFORM_SURFACEABLE_PROPERTY) + NOT_PLATFORM_SURFACEABLE_PROPERTY) .setCardinality( AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) .build()) .addProperty( new AppSearchSchema.DocumentPropertyConfig.Builder( - PACKAGE_ACCESSIBLE_PROPERTY) - .setSchemaType(PACKAGE_ACCESSIBLE_TYPE) + PACKAGE_ACCESSIBLE_PROPERTY, PACKAGE_ACCESSIBLE_TYPE) .setCardinality( AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) .build()) @@ -160,9 +159,9 @@ public class VisibilityStore { private static final String NAMESPACE = ""; /** - * Prefix to add to all visibility document uri's. IcingSearchEngine doesn't allow empty uri's. + * Prefix to add to all visibility document ids. IcingSearchEngine doesn't allow empty ids. */ - private static final String URI_PREFIX = "uri:"; + private static final String ID_PREFIX = "uri:"; private final AppSearchImpl mAppSearchImpl; @@ -259,13 +258,13 @@ public class VisibilityStore { } try { - // Note: We use the other clients' prefixed names as uris + // Note: We use the other clients' prefixed names as ids GenericDocument document = mAppSearchImpl.getDocument( PACKAGE_NAME, DATABASE_NAME, NAMESPACE, - /*uri=*/ addUriPrefix(prefix), + /*id=*/ addIdPrefix(prefix), /*typePropertyPaths=*/ Collections.emptyMap()); // Update platform visibility settings @@ -340,7 +339,7 @@ public class VisibilityStore { // Persist the document GenericDocument.Builder<?> visibilityDocument = new GenericDocument.Builder<>( - NAMESPACE, /*uri=*/ addUriPrefix(prefix), VISIBILITY_TYPE); + NAMESPACE, /*id=*/ addIdPrefix(prefix), VISIBILITY_TYPE); if (!schemasNotPlatformSurfaceable.isEmpty()) { visibilityDocument.setPropertyString( NOT_PLATFORM_SURFACEABLE_PROPERTY, @@ -353,7 +352,7 @@ public class VisibilityStore { schemasPackageAccessible.entrySet()) { for (int i = 0; i < entry.getValue().size(); i++) { GenericDocument packageAccessibleDocument = new GenericDocument.Builder<>( - NAMESPACE, /*uri=*/ "", PACKAGE_ACCESSIBLE_TYPE) + NAMESPACE, /*id=*/ "", PACKAGE_ACCESSIBLE_TYPE) .setPropertyString( PACKAGE_NAME_PROPERTY, entry.getValue().get(i).getPackageName()) @@ -480,13 +479,13 @@ public class VisibilityStore { } /** - * Adds a uri prefix to create a visibility store document's uri. + * Adds a prefix to create a visibility store document's id. * - * @param uri Non-prefixed uri - * @return Prefixed uri + * @param id Non-prefixed id + * @return Prefixed id */ - private static String addUriPrefix(String uri) { - return URI_PREFIX + uri; + private static String addIdPrefix(String id) { + return ID_PREFIX + id; } /** diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java index 50ac054a05fc..98fcb1370d8b 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java @@ -30,7 +30,7 @@ import android.annotation.Nullable; import android.annotation.WorkerThread; import android.app.appsearch.AppSearchSchema; import android.app.appsearch.GenericDocument; -import android.app.appsearch.GetByUriRequest; +import android.app.appsearch.GetByDocumentIdRequest; import android.app.appsearch.GetSchemaResponse; import android.app.appsearch.PackageIdentifier; import android.app.appsearch.SearchResultPage; @@ -573,14 +573,14 @@ public final class AppSearchImpl implements Closeable { } /** - * Retrieves a document from the AppSearch index by URI. + * Retrieves a document from the AppSearch index by namespace and document ID. * * <p>This method belongs to query group. * * @param packageName The package that owns this document. * @param databaseName The databaseName this document resides in. * @param namespace The namespace this document resides in. - * @param uri The URI of the document to get. + * @param id The ID of the document to get. * @param typePropertyPaths A map of schema type to a list of property paths to return in the * result. * @return The Document contents @@ -591,7 +591,7 @@ public final class AppSearchImpl implements Closeable { @NonNull String packageName, @NonNull String databaseName, @NonNull String namespace, - @NonNull String uri, + @NonNull String id, @NonNull Map<String, List<String>> typePropertyPaths) throws AppSearchException { mReadWriteLock.readLock().lock(); @@ -606,7 +606,8 @@ public final class AppSearchImpl implements Closeable { TypePropertyMask typePropertyMask = nonPrefixedPropertyMasks.get(i); String nonPrefixedType = typePropertyMask.getSchemaType(); String prefixedType = - nonPrefixedType.equals(GetByUriRequest.PROJECTION_SCHEMA_TYPE_WILDCARD) + nonPrefixedType.equals( + GetByDocumentIdRequest.PROJECTION_SCHEMA_TYPE_WILDCARD) ? nonPrefixedType : prefix + nonPrefixedType; prefixedPropertyMasks.add( @@ -618,7 +619,7 @@ public final class AppSearchImpl implements Closeable { .build(); GetResultProto getResultProto = - mIcingSearchEngineLocked.get(prefix + namespace, uri, getResultSpec); + mIcingSearchEngineLocked.get(prefix + namespace, id, getResultSpec); checkSuccess(getResultProto.getStatus()); // The schema type map cannot be null at this point. It could only be null if no @@ -881,7 +882,7 @@ public final class AppSearchImpl implements Closeable { @NonNull String packageName, @NonNull String databaseName, @NonNull String namespace, - @NonNull String uri, + @NonNull String documentId, long usageTimestampMillis, boolean systemUsage) throws AppSearchException { @@ -897,7 +898,7 @@ public final class AppSearchImpl implements Closeable { UsageReport report = UsageReport.newBuilder() .setDocumentNamespace(prefixedNamespace) - .setDocumentUri(uri) + .setDocumentUri(documentId) .setUsageTimestampMs(usageTimestampMillis) .setUsageType(usageType) .build(); @@ -910,21 +911,21 @@ public final class AppSearchImpl implements Closeable { } /** - * Removes the given document by URI. + * Removes the given document by id. * * <p>This method belongs to mutate group. * * @param packageName The package name that owns the document. * @param databaseName The databaseName the document is in. * @param namespace Namespace of the document to remove. - * @param uri URI of the document to remove. + * @param id ID of the document to remove. * @throws AppSearchException on IcingSearchEngine error. */ public void remove( @NonNull String packageName, @NonNull String databaseName, @NonNull String namespace, - @NonNull String uri) + @NonNull String id) throws AppSearchException { mReadWriteLock.writeLock().lock(); try { @@ -932,7 +933,7 @@ public final class AppSearchImpl implements Closeable { String prefixedNamespace = createPrefix(packageName, databaseName) + namespace; DeleteResultProto deleteResultProto = - mIcingSearchEngineLocked.delete(prefixedNamespace, uri); + mIcingSearchEngineLocked.delete(prefixedNamespace, id); checkSuccess(deleteResultProto.getStatus()); } finally { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java index 5ff56abd870a..0cdad37d2080 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java @@ -52,7 +52,7 @@ public final class GenericDocumentToProtoConverter { Objects.requireNonNull(document); DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder(); mProtoBuilder - .setUri(document.getUri()) + .setUri(document.getId()) .setSchema(document.getSchemaType()) .setNamespace(document.getNamespace()) .setScore(document.getScore()) diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java index e3fa7e08aac4..80f700746b35 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java @@ -104,7 +104,7 @@ public final class SchemaToProtoConverter { .setDocumentIndexingConfig( DocumentIndexingConfig.newBuilder() .setIndexNestedProperties( - documentProperty.isIndexNestedProperties())); + documentProperty.shouldIndexNestedProperties())); } return builder.build(); } @@ -174,10 +174,10 @@ public final class SchemaToProtoConverter { @NonNull private static AppSearchSchema.DocumentPropertyConfig toDocumentPropertyConfig( @NonNull PropertyConfigProto proto) { - return new AppSearchSchema.DocumentPropertyConfig.Builder(proto.getPropertyName()) + return new AppSearchSchema.DocumentPropertyConfig.Builder( + proto.getPropertyName(), proto.getSchemaType()) .setCardinality(proto.getCardinality().getNumber()) - .setSchemaType(proto.getSchemaType()) - .setIndexNestedProperties( + .setShouldIndexNestedProperties( proto.getDocumentIndexingConfig().getIndexNestedProperties()) .build(); } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java index 57c1590d2bef..84220d72cbab 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java @@ -125,8 +125,7 @@ public class SearchResultToProtoConverter { private static SearchResult.MatchInfo toMatchInfo( @NonNull SnippetMatchProto snippetMatchProto, @NonNull String propertyPath) { - return new SearchResult.MatchInfo.Builder() - .setPropertyPath(propertyPath) + return new SearchResult.MatchInfo.Builder(propertyPath) .setExactMatchRange( new SearchResult.MatchRange( snippetMatchProto.getExactMatchPosition(), diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index f99664b4c685..ce0ebe62f65b 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -If9d1d770d2327d7d0db7d82acfc54787b5de64bc +I06df2c636d26419e653c5d8c9e7d15449da6816e diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java index 61933672a1e1..930cd60beb58 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java @@ -24,10 +24,10 @@ import android.app.appsearch.AppSearchSession; import android.app.appsearch.AppSearchSessionShim; import android.app.appsearch.BatchResultCallback; import android.app.appsearch.GenericDocument; -import android.app.appsearch.GetByUriRequest; +import android.app.appsearch.GetByDocumentIdRequest; import android.app.appsearch.GetSchemaResponse; import android.app.appsearch.PutDocumentsRequest; -import android.app.appsearch.RemoveByUriRequest; +import android.app.appsearch.RemoveByDocumentIdRequest; import android.app.appsearch.ReportUsageRequest; import android.app.appsearch.SearchResults; import android.app.appsearch.SearchResultsShim; @@ -121,11 +121,12 @@ public class AppSearchSessionShimImpl implements AppSearchSessionShim { @Override @NonNull - public ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByUri( - @NonNull GetByUriRequest request) { + public ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByDocumentId( + @NonNull GetByDocumentIdRequest request) { SettableFuture<AppSearchBatchResult<String, GenericDocument>> future = SettableFuture.create(); - mAppSearchSession.getByUri(request, mExecutor, new BatchResultCallbackAdapter<>(future)); + mAppSearchSession.getByDocumentId( + request, mExecutor, new BatchResultCallbackAdapter<>(future)); return future; } @@ -148,7 +149,7 @@ public class AppSearchSessionShimImpl implements AppSearchSessionShim { @Override @NonNull public ListenableFuture<AppSearchBatchResult<String, Void>> remove( - @NonNull RemoveByUriRequest request) { + @NonNull RemoveByDocumentIdRequest request) { SettableFuture<AppSearchBatchResult<String, Void>> future = SettableFuture.create(); mAppSearchSession.remove(request, mExecutor, new BatchResultCallbackAdapter<>(future)); return future; diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java index 494945db5655..345b059d037c 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java @@ -25,10 +25,14 @@ import java.io.Closeable; import java.util.Set; /** - * Represents a connection to an AppSearch storage system where {@link GenericDocument}s can be - * placed and queried. + * Provides a connection to a single AppSearch database. + * + * <p>An {@link AppSearchSessionShim} instance provides access to database operations such as + * setting a schema, adding documents, and searching. * * <p>All implementations of this interface must be thread safe. + * + * @see GlobalSearchSessionShim */ public interface AppSearchSessionShim extends Closeable { @@ -76,28 +80,29 @@ public interface AppSearchSessionShim extends Closeable { * * @param request containing documents to be indexed. * @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The - * keys of the returned {@link AppSearchBatchResult} are the URIs of the input documents. - * The values are either {@code null} if the corresponding document was successfully - * indexed, or a failed {@link AppSearchResult} otherwise. + * keys of the returned {@link AppSearchBatchResult} are the IDs of the input documents. The + * values are either {@code null} if the corresponding document was successfully indexed, or + * a failed {@link AppSearchResult} otherwise. */ @NonNull ListenableFuture<AppSearchBatchResult<String, Void>> put(@NonNull PutDocumentsRequest request); /** - * Gets {@link GenericDocument} objects by URIs and namespace from the {@link + * Gets {@link GenericDocument} objects by document IDs in a namespace from the {@link * AppSearchSessionShim} database. * - * @param request a request containing URIs and namespace to get documents for. + * @param request a request containing a namespace and IDs to get documents for. * @return A {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The - * keys of the {@link AppSearchBatchResult} represent the input URIs from the {@link - * GetByUriRequest} object. The values are either the corresponding {@link GenericDocument} - * object for the URI on success, or an {@link AppSearchResult} object on failure. For - * example, if a URI is not found, the value for that URI will be set to an {@link - * AppSearchResult} object with result code: {@link AppSearchResult#RESULT_NOT_FOUND}. + * keys of the {@link AppSearchBatchResult} represent the input document IDs from the {@link + * GetByDocumentIdRequest} object. The values are either the corresponding {@link + * GenericDocument} object for the ID on success, or an {@link AppSearchResult} object on + * failure. For example, if an ID is not found, the value for that ID will be set to an + * {@link AppSearchResult} object with result code: {@link + * AppSearchResult#RESULT_NOT_FOUND}. */ @NonNull - ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByUri( - @NonNull GetByUriRequest request); + ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByDocumentId( + @NonNull GetByDocumentIdRequest request); /** * Retrieves documents from the open {@link AppSearchSessionShim} that match a given query @@ -162,7 +167,7 @@ public interface AppSearchSessionShim extends Closeable { SearchResultsShim search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec); /** - * Reports usage of a particular document by URI and namespace. + * Reports usage of a particular document by namespace and ID. * * <p>A usage report represents an event in which a user interacted with or viewed a document. * @@ -181,25 +186,26 @@ public interface AppSearchSessionShim extends Closeable { ListenableFuture<Void> reportUsage(@NonNull ReportUsageRequest request); /** - * Removes {@link GenericDocument} objects by URIs and namespace from the {@link + * Removes {@link GenericDocument} objects by document IDs in a namespace from the {@link * AppSearchSessionShim} database. * - * <p>Removed documents will no longer be surfaced by {@link #search} or {@link #getByUri} - * calls. + * <p>Removed documents will no longer be surfaced by {@link #search} or {@link + * #getByDocumentId} calls. * * <p>Once the database crosses the document count or byte usage threshold, removed documents * will be deleted from disk. * - * @param request {@link RemoveByUriRequest} with URIs and namespace to remove from the index. + * @param request {@link RemoveByDocumentIdRequest} with IDs in a namespace to remove from the + * index. * @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The - * keys of the {@link AppSearchBatchResult} represent the input URIs from the {@link - * RemoveByUriRequest} object. The values are either {@code null} on success, or a failed - * {@link AppSearchResult} otherwise. URIs that are not found will return a failed {@link - * AppSearchResult} with a result code of {@link AppSearchResult#RESULT_NOT_FOUND}. + * keys of the {@link AppSearchBatchResult} represent the input IDs from the {@link + * RemoveByDocumentIdRequest} object. The values are either {@code null} on success, or a + * failed {@link AppSearchResult} otherwise. IDs that are not found will return a failed + * {@link AppSearchResult} with a result code of {@link AppSearchResult#RESULT_NOT_FOUND}. */ @NonNull ListenableFuture<AppSearchBatchResult<String, Void>> remove( - @NonNull RemoveByUriRequest request); + @NonNull RemoveByDocumentIdRequest request); /** * Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java index fd4734cea65f..ec9a42eaa276 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java @@ -22,7 +22,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import android.app.appsearch.AppSearchBatchResult; import android.app.appsearch.AppSearchSessionShim; import android.app.appsearch.GenericDocument; -import android.app.appsearch.GetByUriRequest; +import android.app.appsearch.GetByDocumentIdRequest; import android.app.appsearch.SearchResult; import android.app.appsearch.SearchResultsShim; @@ -43,30 +43,30 @@ public class AppSearchTestUtils { } public static List<GenericDocument> doGet( - AppSearchSessionShim session, String namespace, String... uris) throws Exception { + AppSearchSessionShim session, String namespace, String... ids) throws Exception { AppSearchBatchResult<String, GenericDocument> result = checkIsBatchResultSuccess( - session.getByUri( - new GetByUriRequest.Builder(namespace).addUris(uris).build())); - assertThat(result.getSuccesses()).hasSize(uris.length); + session.getByDocumentId( + new GetByDocumentIdRequest.Builder(namespace).addIds(ids).build())); + assertThat(result.getSuccesses()).hasSize(ids.length); assertThat(result.getFailures()).isEmpty(); - List<GenericDocument> list = new ArrayList<>(uris.length); - for (String uri : uris) { - list.add(result.getSuccesses().get(uri)); + List<GenericDocument> list = new ArrayList<>(ids.length); + for (String id : ids) { + list.add(result.getSuccesses().get(id)); } return list; } - public static List<GenericDocument> doGet(AppSearchSessionShim session, GetByUriRequest request) - throws Exception { + public static List<GenericDocument> doGet( + AppSearchSessionShim session, GetByDocumentIdRequest request) throws Exception { AppSearchBatchResult<String, GenericDocument> result = - checkIsBatchResultSuccess(session.getByUri(request)); - Set<String> uris = request.getUris(); - assertThat(result.getSuccesses()).hasSize(uris.size()); + checkIsBatchResultSuccess(session.getByDocumentId(request)); + Set<String> ids = request.getIds(); + assertThat(result.getSuccesses()).hasSize(ids.size()); assertThat(result.getFailures()).isEmpty(); - List<GenericDocument> list = new ArrayList<>(uris.size()); - for (String uri : uris) { - list.add(result.getSuccesses().get(uri)); + List<GenericDocument> list = new ArrayList<>(ids.size()); + for (String id : ids) { + list.add(result.getSuccesses().get(id)); } return list; } diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java index f39916ee1510..892f84e5bdb8 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java @@ -23,9 +23,12 @@ import com.google.common.util.concurrent.ListenableFuture; import java.io.Closeable; /** - * This class provides global access to the centralized AppSearch index maintained by the system. + * Provides a connection to all AppSearch databases the querying application has been granted access + * to. * - * <p>Apps can retrieve indexed documents through the {@link #search} API. + * <p>All implementations of this interface must be thread safe. + * + * @see AppSearchSessionShim */ public interface GlobalSearchSessionShim extends Closeable { /** diff --git a/core/java/android/content/pm/AppSearchPerson.java b/core/java/android/content/pm/AppSearchPerson.java index 9283e5fd3cbd..98d150b6ffdd 100644 --- a/core/java/android/content/pm/AppSearchPerson.java +++ b/core/java/android/content/pm/AppSearchPerson.java @@ -91,7 +91,7 @@ public class AppSearchPerson extends GenericDocument { String uri; try { uri = UriCodec.decode( - getUri(), false /* convertPlus */, StandardCharsets.UTF_8, + getId(), false /* convertPlus */, StandardCharsets.UTF_8, true /* throwOnFailure */); } catch (IllegalArgumentException e) { uri = null; diff --git a/core/java/android/content/pm/AppSearchShortcutInfo.java b/core/java/android/content/pm/AppSearchShortcutInfo.java index eb50924579f6..afaecec7a29a 100644 --- a/core/java/android/content/pm/AppSearchShortcutInfo.java +++ b/core/java/android/content/pm/AppSearchShortcutInfo.java @@ -153,8 +153,8 @@ public class AppSearchShortcutInfo extends GenericDocument { .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) .build() - ).addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder(KEY_PERSON) - .setSchemaType(AppSearchPerson.SCHEMA_TYPE) + ).addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder( + KEY_PERSON, AppSearchPerson.SCHEMA_TYPE) .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) .build() @@ -419,7 +419,7 @@ public class AppSearchShortcutInfo extends GenericDocument { final String bitmapPath = getPropertyString(KEY_BITMAP_PATH); final int disabledReason = Integer.parseInt(getPropertyString(KEY_DISABLED_REASON)); final ShortcutInfo si = new ShortcutInfo( - userId, getUri(), packageName, activity, icon, shortLabel, shortLabelResId, + userId, getId(), packageName, activity, icon, shortLabel, shortLabelResId, shortLabelResName, longLabel, longLabelResId, longLabelResName, disabledMessage, disabledMessageResId, disabledMessageResName, categoriesSet, intents, rank, extras, getCreationTimestampMillis(), flags, iconResId, iconResName, bitmapPath, iconUri, diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java index ed53d5f39a39..3989ec7c95d6 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java @@ -25,7 +25,7 @@ public class AppSearchEmailTest { @Test public void testBuildEmailAndGetValue() { AppSearchEmail email = - new AppSearchEmail.Builder("namespace", "uri") + new AppSearchEmail.Builder("namespace", "id") .setFrom("FakeFromAddress") .setCc("CC1", "CC2") // Score and Property are mixed into the middle to make sure @@ -38,7 +38,7 @@ public class AppSearchEmailTest { .build(); assertThat(email.getNamespace()).isEqualTo("namespace"); - assertThat(email.getUri()).isEqualTo("uri"); + assertThat(email.getId()).isEqualTo("id"); assertThat(email.getFrom()).isEqualTo("FakeFromAddress"); assertThat(email.getTo()).isNull(); assertThat(email.getCc()).asList().containsExactly("CC1", "CC2"); diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java index b884ddcd1420..6884f13d4cc9 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java @@ -27,13 +27,13 @@ public class GenericDocumentTest { @Test public void testRecreateFromParcel() { GenericDocument inDoc = - new GenericDocument.Builder<>("namespace", "uri1", "schema1") + new GenericDocument.Builder<>("namespace", "id1", "schema1") .setScore(42) .setPropertyString("propString", "Hello") .setPropertyBytes("propBytes", new byte[][] {{1, 2}}) .setPropertyDocument( "propDocument", - new GenericDocument.Builder<>("namespace", "uri2", "schema2") + new GenericDocument.Builder<>("namespace", "id2", "schema2") .setPropertyString("propString", "Goodbye") .setPropertyBytes("propBytes", new byte[][] {{3, 4}}) .build()) diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java index 76372141ca04..a1f7986f62df 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java @@ -36,7 +36,7 @@ public class PutDocumentsRequestTest { PutDocumentsRequest request = new PutDocumentsRequest.Builder().addGenericDocuments(emails).build(); - assertThat(request.getGenericDocuments().get(0).getUri()).isEqualTo("test1"); - assertThat(request.getGenericDocuments().get(1).getUri()).isEqualTo("test2"); + assertThat(request.getGenericDocuments().get(0).getId()).isEqualTo("test1"); + assertThat(request.getGenericDocuments().get(1).getId()).isEqualTo("test2"); } } diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index ada65860018a..b690e6c0b8fa 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -23,10 +23,10 @@ import android.app.appsearch.AppSearchManager; import android.app.appsearch.AppSearchResult; import android.app.appsearch.AppSearchSession; import android.app.appsearch.GenericDocument; -import android.app.appsearch.GetByUriRequest; +import android.app.appsearch.GetByDocumentIdRequest; import android.app.appsearch.PackageIdentifier; import android.app.appsearch.PutDocumentsRequest; -import android.app.appsearch.RemoveByUriRequest; +import android.app.appsearch.RemoveByDocumentIdRequest; import android.app.appsearch.ReportUsageRequest; import android.app.appsearch.SearchResult; import android.app.appsearch.SearchResults; @@ -449,8 +449,8 @@ class ShortcutPackage extends ShortcutPackageItem { session -> { final AndroidFuture<Boolean> future = new AndroidFuture<>(); session.reportUsage( - new ReportUsageRequest.Builder(getPackageName()) - .setUri(newShortcut.getId()).build(), + new ReportUsageRequest.Builder( + getPackageName(), newShortcut.getId()).build(), mShortcutUser.mExecutor, result -> future.complete(result.isSuccess())); return future; @@ -2377,7 +2377,8 @@ class ShortcutPackage extends ShortcutPackageItem { } awaitInAppSearch("Removing shortcut with id=" + id, session -> { final AndroidFuture<Boolean> future = new AndroidFuture<>(); - session.remove(new RemoveByUriRequest.Builder(getPackageName()).addUris(id).build(), + session.remove( + new RemoveByDocumentIdRequest.Builder(getPackageName()).addIds(id).build(), mShortcutUser.mExecutor, result -> { if (!result.isSuccess()) { final Map<String, AppSearchResult<Void>> failures = @@ -2420,8 +2421,9 @@ class ShortcutPackage extends ShortcutPackageItem { } return awaitInAppSearch("Getting shortcut by id", session -> { final AndroidFuture<List<ShortcutInfo>> future = new AndroidFuture<>(); - session.getByUri( - new GetByUriRequest.Builder(getPackageName()).addUris(shortcutIds).build(), + session.getByDocumentId( + new GetByDocumentIdRequest.Builder(getPackageName()) + .addIds(shortcutIds).build(), mShortcutUser.mExecutor, results -> { final List<ShortcutInfo> ret = new ArrayList<>(1); diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java index 380d9be5ae57..9a7cf803763d 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java @@ -307,13 +307,13 @@ public class AppSearchImplTest { public void testAddDocumentTypePrefix() { DocumentProto insideDocument = DocumentProto.newBuilder() - .setUri("inside-uri") + .setUri("inside-id") .setSchema("type") .setNamespace("namespace") .build(); DocumentProto documentProto = DocumentProto.newBuilder() - .setUri("uri") + .setUri("id") .setSchema("type") .setNamespace("namespace") .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) @@ -321,13 +321,13 @@ public class AppSearchImplTest { DocumentProto expectedInsideDocument = DocumentProto.newBuilder() - .setUri("inside-uri") + .setUri("inside-id") .setSchema("package$databaseName/type") .setNamespace("package$databaseName/namespace") .build(); DocumentProto expectedDocumentProto = DocumentProto.newBuilder() - .setUri("uri") + .setUri("id") .setSchema("package$databaseName/type") .setNamespace("package$databaseName/namespace") .addProperties( @@ -344,13 +344,13 @@ public class AppSearchImplTest { public void testRemoveDocumentTypePrefixes() throws Exception { DocumentProto insideDocument = DocumentProto.newBuilder() - .setUri("inside-uri") + .setUri("inside-id") .setSchema("package$databaseName/type") .setNamespace("package$databaseName/namespace") .build(); DocumentProto documentProto = DocumentProto.newBuilder() - .setUri("uri") + .setUri("id") .setSchema("package$databaseName/type") .setNamespace("package$databaseName/namespace") .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) @@ -358,14 +358,14 @@ public class AppSearchImplTest { DocumentProto expectedInsideDocument = DocumentProto.newBuilder() - .setUri("inside-uri") + .setUri("inside-id") .setSchema("type") .setNamespace("namespace") .build(); DocumentProto expectedDocumentProto = DocumentProto.newBuilder() - .setUri("uri") + .setUri("id") .setSchema("type") .setNamespace("namespace") .addProperties( @@ -383,7 +383,7 @@ public class AppSearchImplTest { // Set two different database names in the document, which should never happen DocumentProto documentProto = DocumentProto.newBuilder() - .setUri("uri") + .setUri("id") .setSchema("prefix1/type") .setNamespace("prefix2/namespace") .build(); @@ -401,13 +401,13 @@ public class AppSearchImplTest { // happen. DocumentProto insideDocument = DocumentProto.newBuilder() - .setUri("inside-uri") + .setUri("inside-id") .setSchema("prefix1/type") .setNamespace("prefix1/namespace") .build(); DocumentProto documentProto = DocumentProto.newBuilder() - .setUri("uri") + .setUri("id") .setSchema("prefix2/type") .setNamespace("prefix2/namespace") .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) @@ -441,7 +441,7 @@ public class AppSearchImplTest { + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) { GenericDocument document = - new GenericDocument.Builder<>("namespace", "uri" + i, "type").build(); + new GenericDocument.Builder<>("namespace", "id" + i, "type").build(); mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null); } @@ -452,7 +452,7 @@ public class AppSearchImplTest { // delete 999 documents, we will reach the threshold to trigger optimize() in next // deletion. for (int i = 0; i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1; i++) { - mAppSearchImpl.remove("package", "database", "namespace", "uri" + i); + mAppSearchImpl.remove("package", "database", "namespace", "id" + i); } // Updates the check for optimize counter, checkForOptimize() will be triggered since @@ -472,7 +472,7 @@ public class AppSearchImplTest { < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) { - mAppSearchImpl.remove("package", "database", "namespace", "uri" + i); + mAppSearchImpl.remove("package", "database", "namespace", "id" + i); } // updates the check for optimize counter, will reach both CHECK_OPTIMIZE_INTERVAL and // OPTIMIZE_THRESHOLD_DOC_COUNT this time and trigger a optimize(). @@ -501,8 +501,7 @@ public class AppSearchImplTest { /*version=*/ 0); // Insert document - GenericDocument document = - new GenericDocument.Builder<>("namespace", "uri", "type").build(); + GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build(); mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null); // Rewrite SearchSpec @@ -544,11 +543,11 @@ public class AppSearchImplTest { // Insert documents GenericDocument document1 = - new GenericDocument.Builder<>("namespace", "uri", "typeA").build(); + new GenericDocument.Builder<>("namespace", "id", "typeA").build(); mAppSearchImpl.putDocument("package", "database1", document1, /*logger=*/ null); GenericDocument document2 = - new GenericDocument.Builder<>("namespace", "uri", "typeB").build(); + new GenericDocument.Builder<>("namespace", "id", "typeB").build(); mAppSearchImpl.putDocument("package", "database2", document2, /*logger=*/ null); // Rewrite SearchSpec @@ -587,8 +586,7 @@ public class AppSearchImplTest { /*version=*/ 0); // Insert document - GenericDocument document = - new GenericDocument.Builder<>("namespace", "uri", "type").build(); + GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build(); mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null); // If 'allowedPrefixedSchemas' is empty, this returns false since there's nothing to @@ -642,7 +640,7 @@ public class AppSearchImplTest { // Insert package1 document GenericDocument document = - new GenericDocument.Builder<>("namespace", "uri", "schema1").build(); + new GenericDocument.Builder<>("namespace", "id", "schema1").build(); mAppSearchImpl.putDocument("package1", "database1", document, /*logger=*/ null); // No query filters specified, package2 shouldn't be able to query for package1's documents. @@ -653,7 +651,7 @@ public class AppSearchImplTest { assertThat(searchResultPage.getResults()).isEmpty(); // Insert package2 document - document = new GenericDocument.Builder<>("namespace", "uri", "schema2").build(); + document = new GenericDocument.Builder<>("namespace", "id", "schema2").build(); mAppSearchImpl.putDocument("package2", "database2", document, /*logger=*/ null); // No query filters specified. package2 should only get its own documents back. @@ -694,7 +692,7 @@ public class AppSearchImplTest { // Insert package1 document GenericDocument document = - new GenericDocument.Builder<>("namespace", "uri", "schema1").build(); + new GenericDocument.Builder<>("namespace", "id", "schema1").build(); mAppSearchImpl.putDocument("package1", "database1", document, /*logger=*/ null); // "package1" filter specified, but package2 shouldn't be able to query for package1's @@ -709,7 +707,7 @@ public class AppSearchImplTest { assertThat(searchResultPage.getResults()).isEmpty(); // Insert package2 document - document = new GenericDocument.Builder<>("namespace", "uri", "schema2").build(); + document = new GenericDocument.Builder<>("namespace", "id", "schema2").build(); mAppSearchImpl.putDocument("package2", "database2", document, /*logger=*/ null); // "package2" filter specified, package2 should only get its own documents back. @@ -1028,7 +1026,7 @@ public class AppSearchImplTest { // Insert package document GenericDocument document = - new GenericDocument.Builder<>("namespace", "uri", "schema").build(); + new GenericDocument.Builder<>("namespace", "id", "schema").build(); mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null); // Verify the document is indexed. @@ -1135,14 +1133,14 @@ public class AppSearchImplTest { + PrefixUtil.PACKAGE_DELIMITER + "databaseName" + PrefixUtil.DATABASE_DELIMITER; - final String uri = "uri"; + final String id = "id"; final String namespace = prefix + "namespace"; final String schemaType = prefix + "schema"; // Building the SearchResult received from query. DocumentProto documentProto = DocumentProto.newBuilder() - .setUri(uri) + .setUri(id) .setNamespace(namespace) .setSchema(schemaType) .build(); @@ -1185,32 +1183,32 @@ public class AppSearchImplTest { // Insert two docs GenericDocument document1 = - new GenericDocument.Builder<>("namespace", "uri1", "type").build(); + new GenericDocument.Builder<>("namespace", "id1", "type").build(); GenericDocument document2 = - new GenericDocument.Builder<>("namespace", "uri2", "type").build(); + new GenericDocument.Builder<>("namespace", "id2", "type").build(); mAppSearchImpl.putDocument("package", "database", document1, /*logger=*/ null); mAppSearchImpl.putDocument("package", "database", document2, /*logger=*/ null); - // Report some usages. uri1 has 2 app and 1 system usage, uri2 has 1 app and 2 system usage. + // Report some usages. id1 has 2 app and 1 system usage, id2 has 1 app and 2 system usage. mAppSearchImpl.reportUsage( "package", "database", "namespace", - "uri1", + "id1", /*usageTimestampMillis=*/ 10, /*systemUsage=*/ false); mAppSearchImpl.reportUsage( "package", "database", "namespace", - "uri1", + "id1", /*usageTimestampMillis=*/ 20, /*systemUsage=*/ false); mAppSearchImpl.reportUsage( "package", "database", "namespace", - "uri1", + "id1", /*usageTimestampMillis=*/ 1000, /*systemUsage=*/ true); @@ -1218,25 +1216,25 @@ public class AppSearchImplTest { "package", "database", "namespace", - "uri2", + "id2", /*usageTimestampMillis=*/ 100, /*systemUsage=*/ false); mAppSearchImpl.reportUsage( "package", "database", "namespace", - "uri2", + "id2", /*usageTimestampMillis=*/ 200, /*systemUsage=*/ true); mAppSearchImpl.reportUsage( "package", "database", "namespace", - "uri2", + "id2", /*usageTimestampMillis=*/ 150, /*systemUsage=*/ true); - // Sort by app usage count: uri1 should win + // Sort by app usage count: id1 should win List<SearchResult> page = mAppSearchImpl .query( @@ -1249,10 +1247,10 @@ public class AppSearchImplTest { .build()) .getResults(); assertThat(page).hasSize(2); - assertThat(page.get(0).getGenericDocument().getUri()).isEqualTo("uri1"); - assertThat(page.get(1).getGenericDocument().getUri()).isEqualTo("uri2"); + assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id1"); + assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id2"); - // Sort by app usage timestamp: uri2 should win + // Sort by app usage timestamp: id2 should win page = mAppSearchImpl .query( @@ -1267,10 +1265,10 @@ public class AppSearchImplTest { .build()) .getResults(); assertThat(page).hasSize(2); - assertThat(page.get(0).getGenericDocument().getUri()).isEqualTo("uri2"); - assertThat(page.get(1).getGenericDocument().getUri()).isEqualTo("uri1"); + assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id2"); + assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id1"); - // Sort by system usage count: uri2 should win + // Sort by system usage count: id2 should win page = mAppSearchImpl .query( @@ -1284,10 +1282,10 @@ public class AppSearchImplTest { .build()) .getResults(); assertThat(page).hasSize(2); - assertThat(page.get(0).getGenericDocument().getUri()).isEqualTo("uri2"); - assertThat(page.get(1).getGenericDocument().getUri()).isEqualTo("uri1"); + assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id2"); + assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id1"); - // Sort by system usage timestamp: uri1 should win + // Sort by system usage timestamp: id1 should win page = mAppSearchImpl .query( @@ -1302,8 +1300,8 @@ public class AppSearchImplTest { .build()) .getResults(); assertThat(page).hasSize(2); - assertThat(page.get(0).getGenericDocument().getUri()).isEqualTo("uri1"); - assertThat(page.get(1).getGenericDocument().getUri()).isEqualTo("uri2"); + assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id1"); + assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id2"); } @Test @@ -1353,7 +1351,7 @@ public class AppSearchImplTest { // Insert document for "package1" GenericDocument document = - new GenericDocument.Builder<>("namespace", "uri1", "type").build(); + new GenericDocument.Builder<>("namespace", "id1", "type").build(); mAppSearchImpl.putDocument("package1", "database", document, /*logger=*/ null); // Insert schema for "package2" @@ -1367,9 +1365,9 @@ public class AppSearchImplTest { /*version=*/ 0); // Insert two documents for "package2" - document = new GenericDocument.Builder<>("namespace", "uri1", "type").build(); + document = new GenericDocument.Builder<>("namespace", "id1", "type").build(); mAppSearchImpl.putDocument("package2", "database", document, /*logger=*/ null); - document = new GenericDocument.Builder<>("namespace", "uri2", "type").build(); + document = new GenericDocument.Builder<>("namespace", "id2", "type").build(); mAppSearchImpl.putDocument("package2", "database", document, /*logger=*/ null); StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForPackage("package1"); @@ -1467,13 +1465,13 @@ public class AppSearchImplTest { // Add a document for "package1", "database1" GenericDocument document = - new GenericDocument.Builder<>("namespace1", "uri1", "type").build(); + new GenericDocument.Builder<>("namespace1", "id1", "type").build(); mAppSearchImpl.putDocument("package1", "database1", document, /*logger=*/ null); // Add two documents for "package1", "database2" - document = new GenericDocument.Builder<>("namespace1", "uri1", "type").build(); + document = new GenericDocument.Builder<>("namespace1", "id1", "type").build(); mAppSearchImpl.putDocument("package1", "database2", document, /*logger=*/ null); - document = new GenericDocument.Builder<>("namespace1", "uri2", "type").build(); + document = new GenericDocument.Builder<>("namespace1", "id2", "type").build(); mAppSearchImpl.putDocument("package1", "database2", document, /*logger=*/ null); StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForDatabase("package1", "database1"); @@ -1543,7 +1541,7 @@ public class AppSearchImplTest { appSearchImpl.putDocument( "package", "database", - new GenericDocument.Builder<>("namespace", "uri", "type").build(), + new GenericDocument.Builder<>("namespace", "id", "type").build(), /*logger=*/ null); }); @@ -1551,7 +1549,7 @@ public class AppSearchImplTest { IllegalStateException.class, () -> { appSearchImpl.getDocument( - "package", "database", "namespace", "uri", Collections.emptyMap()); + "package", "database", "namespace", "id", Collections.emptyMap()); }); expectThrows( @@ -1597,7 +1595,7 @@ public class AppSearchImplTest { "package", "database", "namespace", - "uri", + "id", /*usageTimestampMillis=*/ 1000L, /*systemUsage=*/ false); }); @@ -1605,7 +1603,7 @@ public class AppSearchImplTest { expectThrows( IllegalStateException.class, () -> { - appSearchImpl.remove("package", "database", "namespace", "uri"); + appSearchImpl.remove("package", "database", "namespace", "id"); }); expectThrows( @@ -1664,13 +1662,13 @@ public class AppSearchImplTest { // Add a document and persist it. GenericDocument document = - new GenericDocument.Builder<>("namespace1", "uri1", "type").build(); + new GenericDocument.Builder<>("namespace1", "id1", "type").build(); appSearchImpl.putDocument("package", "database", document, /*logger=*/ null); appSearchImpl.persistToDisk(PersistType.Code.LITE); GenericDocument getResult = appSearchImpl.getDocument( - "package", "database", "namespace1", "uri1", Collections.emptyMap()); + "package", "database", "namespace1", "id1", Collections.emptyMap()); assertThat(getResult).isEqualTo(document); // That document should be visible even from another instance. @@ -1682,7 +1680,7 @@ public class AppSearchImplTest { /*globalQuerierPackage=*/ ""); getResult = appSearchImpl2.getDocument( - "package", "database", "namespace1", "uri1", Collections.emptyMap()); + "package", "database", "namespace1", "id1", Collections.emptyMap()); assertThat(getResult).isEqualTo(document); } @@ -1711,24 +1709,24 @@ public class AppSearchImplTest { // Add two documents and persist them. GenericDocument document1 = - new GenericDocument.Builder<>("namespace1", "uri1", "type").build(); + new GenericDocument.Builder<>("namespace1", "id1", "type").build(); appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null); GenericDocument document2 = - new GenericDocument.Builder<>("namespace1", "uri2", "type").build(); + new GenericDocument.Builder<>("namespace1", "id2", "type").build(); appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null); appSearchImpl.persistToDisk(PersistType.Code.LITE); GenericDocument getResult = appSearchImpl.getDocument( - "package", "database", "namespace1", "uri1", Collections.emptyMap()); + "package", "database", "namespace1", "id1", Collections.emptyMap()); assertThat(getResult).isEqualTo(document1); getResult = appSearchImpl.getDocument( - "package", "database", "namespace1", "uri2", Collections.emptyMap()); + "package", "database", "namespace1", "id2", Collections.emptyMap()); assertThat(getResult).isEqualTo(document2); // Delete the first document - appSearchImpl.remove("package", "database", "namespace1", "uri1"); + appSearchImpl.remove("package", "database", "namespace1", "id1"); appSearchImpl.persistToDisk(PersistType.Code.LITE); expectThrows( AppSearchException.class, @@ -1737,11 +1735,11 @@ public class AppSearchImplTest { "package", "database", "namespace1", - "uri1", + "id1", Collections.emptyMap())); getResult = appSearchImpl.getDocument( - "package", "database", "namespace1", "uri2", Collections.emptyMap()); + "package", "database", "namespace1", "id2", Collections.emptyMap()); assertThat(getResult).isEqualTo(document2); // Only the second document should be retrievable from another instance. @@ -1758,11 +1756,11 @@ public class AppSearchImplTest { "package", "database", "namespace1", - "uri1", + "id1", Collections.emptyMap())); getResult = appSearchImpl2.getDocument( - "package", "database", "namespace1", "uri2", Collections.emptyMap()); + "package", "database", "namespace1", "id2", Collections.emptyMap()); assertThat(getResult).isEqualTo(document2); } @@ -1791,20 +1789,20 @@ public class AppSearchImplTest { // Add two documents and persist them. GenericDocument document1 = - new GenericDocument.Builder<>("namespace1", "uri1", "type").build(); + new GenericDocument.Builder<>("namespace1", "id1", "type").build(); appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null); GenericDocument document2 = - new GenericDocument.Builder<>("namespace2", "uri2", "type").build(); + new GenericDocument.Builder<>("namespace2", "id2", "type").build(); appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null); appSearchImpl.persistToDisk(PersistType.Code.LITE); GenericDocument getResult = appSearchImpl.getDocument( - "package", "database", "namespace1", "uri1", Collections.emptyMap()); + "package", "database", "namespace1", "id1", Collections.emptyMap()); assertThat(getResult).isEqualTo(document1); getResult = appSearchImpl.getDocument( - "package", "database", "namespace2", "uri2", Collections.emptyMap()); + "package", "database", "namespace2", "id2", Collections.emptyMap()); assertThat(getResult).isEqualTo(document2); // Delete the first document @@ -1824,11 +1822,11 @@ public class AppSearchImplTest { "package", "database", "namespace1", - "uri1", + "id1", Collections.emptyMap())); getResult = appSearchImpl.getDocument( - "package", "database", "namespace2", "uri2", Collections.emptyMap()); + "package", "database", "namespace2", "id2", Collections.emptyMap()); assertThat(getResult).isEqualTo(document2); // Only the second document should be retrievable from another instance. @@ -1845,11 +1843,11 @@ public class AppSearchImplTest { "package", "database", "namespace1", - "uri1", + "id1", Collections.emptyMap())); getResult = appSearchImpl2.getDocument( - "package", "database", "namespace2", "uri2", Collections.emptyMap()); + "package", "database", "namespace2", "id2", Collections.emptyMap()); assertThat(getResult).isEqualTo(document2); } } diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java index 673b7ee24eff..1194e76ee2b5 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java @@ -129,8 +129,7 @@ public class AppSearchLoggerTest { /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, /*version=*/ 0); - GenericDocument document = - new GenericDocument.Builder<>("namespace", "uri", "type").build(); + GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build(); mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger); diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java index 63f031722ede..ada49ff4b97a 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java @@ -64,7 +64,7 @@ public class GenericDocumentToProtoConverterTest { public void testDocumentProtoConvert() { GenericDocument document = new GenericDocument.Builder<GenericDocument.Builder<?>>( - "namespace", "uri1", SCHEMA_TYPE_1) + "namespace", "id1", SCHEMA_TYPE_1) .setCreationTimestampMillis(5L) .setScore(1) .setTtlMillis(1L) @@ -80,7 +80,7 @@ public class GenericDocumentToProtoConverterTest { // Create the Document proto. Need to sort the property order by key. DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder() - .setUri("uri1") + .setUri("id1") .setSchema(SCHEMA_TYPE_1) .setCreationTimestampMs(5L) .setScore(1) @@ -140,7 +140,7 @@ public class GenericDocumentToProtoConverterTest { String emptyStringPropertyName = "emptyStringProperty"; DocumentProto documentProto = DocumentProto.newBuilder() - .setUri("uri1") + .setUri("id1") .setSchema(SCHEMA_TYPE_1) .setCreationTimestampMs(5L) .setNamespace("namespace") @@ -167,7 +167,7 @@ public class GenericDocumentToProtoConverterTest { GenericDocument expectedDocument = new GenericDocument.Builder<GenericDocument.Builder<?>>( - "namespace", "uri1", SCHEMA_TYPE_1) + "namespace", "id1", SCHEMA_TYPE_1) .setCreationTimestampMillis(5L) .setPropertyString(emptyStringPropertyName) .build(); @@ -181,7 +181,7 @@ public class GenericDocumentToProtoConverterTest { String documentPropertyName = "documentProperty"; DocumentProto nestedDocumentProto = DocumentProto.newBuilder() - .setUri("uri2") + .setUri("id2") .setSchema(SCHEMA_TYPE_2) .setCreationTimestampMs(5L) .setNamespace("namespace") @@ -190,7 +190,7 @@ public class GenericDocumentToProtoConverterTest { .build(); DocumentProto documentProto = DocumentProto.newBuilder() - .setUri("uri1") + .setUri("id1") .setSchema(SCHEMA_TYPE_1) .setCreationTimestampMs(5L) .setNamespace("namespace") @@ -236,12 +236,12 @@ public class GenericDocumentToProtoConverterTest { GenericDocument expectedDocument = new GenericDocument.Builder<GenericDocument.Builder<?>>( - "namespace", "uri1", SCHEMA_TYPE_1) + "namespace", "id1", SCHEMA_TYPE_1) .setCreationTimestampMillis(5L) .setPropertyDocument( documentPropertyName, new GenericDocument.Builder<GenericDocument.Builder<?>>( - "namespace", "uri2", SCHEMA_TYPE_2) + "namespace", "id2", SCHEMA_TYPE_2) .setCreationTimestampMillis(5L) .setPropertyString(emptyStringPropertyName) .build()) diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java index 26fac492ccd2..d0ce31720fe3 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java @@ -54,22 +54,19 @@ public class SnippetTest { "A commonly used fake word is foo.\n" + " Another nonsense word that’s used a lot\n" + " is bar.\n"; - final String uri = "uri1"; - final String searchWord = "foo"; + final String id = "id1"; final String exactMatch = "foo"; final String window = "is foo"; // Building the SearchResult received from query. - PropertyProto property = - PropertyProto.newBuilder() - .setName(propertyKeyString) - .addStringValues(propertyValueString) - .build(); DocumentProto documentProto = DocumentProto.newBuilder() - .setUri(uri) + .setUri(id) .setSchema(SCHEMA_TYPE) - .addProperties(property) + .addProperties( + PropertyProto.newBuilder() + .setName(propertyKeyString) + .addStringValues(propertyValueString)) .build(); SnippetProto snippetProto = SnippetProto.newBuilder() @@ -81,17 +78,15 @@ public class SnippetTest { .setExactMatchPosition(29) .setExactMatchBytes(3) .setWindowPosition(26) - .setWindowBytes(6) - .build()) - .build()) - .build(); - SearchResultProto.ResultProto resultProto = - SearchResultProto.ResultProto.newBuilder() - .setDocument(documentProto) - .setSnippet(snippetProto) + .setWindowBytes(6))) .build(); SearchResultProto searchResultProto = - SearchResultProto.newBuilder().addResults(resultProto).build(); + SearchResultProto.newBuilder() + .addResults( + SearchResultProto.ResultProto.newBuilder() + .setDocument(documentProto) + .setSnippet(snippetProto)) + .build(); // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = @@ -100,50 +95,45 @@ public class SnippetTest { Collections.singletonList(PACKAGE_NAME), Collections.singletonList(DATABASE_NAME), SCHEMA_MAP); - for (SearchResult result : searchResultPage.getResults()) { - SearchResult.MatchInfo match = result.getMatches().get(0); - assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString); - assertThat(match.getFullText()).isEqualTo(propertyValueString); - assertThat(match.getExactMatch()).isEqualTo(exactMatch); - assertThat(match.getExactMatchRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 29, /*upper=*/ 32)); - assertThat(match.getFullText()).isEqualTo(propertyValueString); - assertThat(match.getSnippetRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 26, /*upper=*/ 32)); - assertThat(match.getSnippet()).isEqualTo(window); - } + assertThat(searchResultPage.getResults()).hasSize(1); + SearchResult.MatchInfo match = searchResultPage.getResults().get(0).getMatches().get(0); + assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString); + assertThat(match.getFullText()).isEqualTo(propertyValueString); + assertThat(match.getExactMatch()).isEqualTo(exactMatch); + assertThat(match.getExactMatchRange()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 29, /*upper=*/ 32)); + assertThat(match.getFullText()).isEqualTo(propertyValueString); + assertThat(match.getSnippetRange()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 26, /*upper=*/ 32)); + assertThat(match.getSnippet()).isEqualTo(window); } // TODO(tytytyww): Add tests for Double and Long Snippets. @Test - public void testNoSnippets() throws Exception { - + public void testNoSnippets() { final String propertyKeyString = "content"; final String propertyValueString = "A commonly used fake word is foo.\n" + " Another nonsense word that’s used a lot\n" + " is bar.\n"; - final String uri = "uri1"; - final String searchWord = "foo"; - final String exactMatch = "foo"; - final String window = "is foo"; + final String id = "id1"; // Building the SearchResult received from query. - PropertyProto property = - PropertyProto.newBuilder() - .setName(propertyKeyString) - .addStringValues(propertyValueString) - .build(); DocumentProto documentProto = DocumentProto.newBuilder() - .setUri(uri) + .setUri(id) .setSchema(SCHEMA_TYPE) - .addProperties(property) + .addProperties( + PropertyProto.newBuilder() + .setName(propertyKeyString) + .addStringValues(propertyValueString)) .build(); - SearchResultProto.ResultProto resultProto = - SearchResultProto.ResultProto.newBuilder().setDocument(documentProto).build(); SearchResultProto searchResultProto = - SearchResultProto.newBuilder().addResults(resultProto).build(); + SearchResultProto.newBuilder() + .addResults( + SearchResultProto.ResultProto.newBuilder() + .setDocument(documentProto)) + .build(); SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage( @@ -151,65 +141,137 @@ public class SnippetTest { Collections.singletonList(PACKAGE_NAME), Collections.singletonList(DATABASE_NAME), SCHEMA_MAP); - for (SearchResult result : searchResultPage.getResults()) { - assertThat(result.getMatches()).isEmpty(); - } + assertThat(searchResultPage.getResults()).hasSize(1); + assertThat(searchResultPage.getResults().get(0).getMatches()).isEmpty(); } @Test - public void testMultipleStringSnippet() throws Exception { - final String searchWord = "Test"; - + public void testMultipleStringSnippet() { // Building the SearchResult received from query. - PropertyProto property1 = - PropertyProto.newBuilder() - .setName("sender.name") - .addStringValues("Test Name Jr.") - .build(); - PropertyProto property2 = - PropertyProto.newBuilder() - .setName("sender.email") - .addStringValues("TestNameJr@gmail.com") - .build(); DocumentProto documentProto = DocumentProto.newBuilder() .setUri("uri1") .setSchema(SCHEMA_TYPE) - .addProperties(property1) - .addProperties(property2) + .addProperties( + PropertyProto.newBuilder() + .setName("senderName") + .addStringValues("Test Name Jr.")) + .addProperties( + PropertyProto.newBuilder() + .setName("senderEmail") + .addStringValues("TestNameJr@gmail.com")) .build(); SnippetProto snippetProto = SnippetProto.newBuilder() .addEntries( SnippetProto.EntryProto.newBuilder() - .setPropertyName("sender.name") + .setPropertyName("senderName") .addSnippetMatches( SnippetMatchProto.newBuilder() .setExactMatchPosition(0) .setExactMatchBytes(4) .setWindowPosition(0) - .setWindowBytes(9) - .build()) - .build()) + .setWindowBytes(9))) .addEntries( SnippetProto.EntryProto.newBuilder() - .setPropertyName("sender.email") + .setPropertyName("senderEmail") .addSnippetMatches( SnippetMatchProto.newBuilder() .setExactMatchPosition(0) .setExactMatchBytes(20) .setWindowPosition(0) - .setWindowBytes(20) - .build()) - .build()) + .setWindowBytes(20))) .build(); - SearchResultProto.ResultProto resultProto = - SearchResultProto.ResultProto.newBuilder() - .setDocument(documentProto) - .setSnippet(snippetProto) + SearchResultProto searchResultProto = + SearchResultProto.newBuilder() + .addResults( + SearchResultProto.ResultProto.newBuilder() + .setDocument(documentProto) + .setSnippet(snippetProto)) + .build(); + + // Making ResultReader and getting Snippet values. + SearchResultPage searchResultPage = + SearchResultToProtoConverter.toSearchResultPage( + searchResultProto, + Collections.singletonList(PACKAGE_NAME), + Collections.singletonList(DATABASE_NAME), + SCHEMA_MAP); + assertThat(searchResultPage.getResults()).hasSize(1); + SearchResult.MatchInfo match1 = searchResultPage.getResults().get(0).getMatches().get(0); + assertThat(match1.getPropertyPath()).isEqualTo("senderName"); + assertThat(match1.getFullText()).isEqualTo("Test Name Jr."); + assertThat(match1.getExactMatchRange()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 4)); + assertThat(match1.getExactMatch()).isEqualTo("Test"); + assertThat(match1.getSnippetRange()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9)); + assertThat(match1.getSnippet()).isEqualTo("Test Name"); + + SearchResult.MatchInfo match2 = searchResultPage.getResults().get(0).getMatches().get(1); + assertThat(match2.getPropertyPath()).isEqualTo("senderEmail"); + assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com"); + assertThat(match2.getExactMatchRange()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20)); + assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com"); + assertThat(match2.getSnippetRange()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20)); + assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com"); + } + + @Test + public void testNestedDocumentSnippet() { + // Building the SearchResult received from query. + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri("id1") + .setSchema(SCHEMA_TYPE) + .addProperties( + PropertyProto.newBuilder() + .setName("sender") + .addDocumentValues( + DocumentProto.newBuilder() + .addProperties( + PropertyProto.newBuilder() + .setName("name") + .addStringValues( + "Test Name Jr.")) + .addProperties( + PropertyProto.newBuilder() + .setName("email") + .addStringValues( + "TestNameJr@gmail.com") + .addStringValues( + "TestNameJr2@gmail.com")))) + .build(); + SnippetProto snippetProto = + SnippetProto.newBuilder() + .addEntries( + SnippetProto.EntryProto.newBuilder() + .setPropertyName("sender.name") + .addSnippetMatches( + SnippetMatchProto.newBuilder() + .setExactMatchPosition(0) + .setExactMatchBytes(4) + .setWindowPosition(0) + .setWindowBytes(9))) + .addEntries( + SnippetProto.EntryProto.newBuilder() + .setPropertyName("sender.email[1]") + .addSnippetMatches( + SnippetMatchProto.newBuilder() + .setExactMatchPosition(0) + .setExactMatchBytes(21) + .setWindowPosition(0) + .setWindowBytes(21))) .build(); SearchResultProto searchResultProto = - SearchResultProto.newBuilder().addResults(resultProto).build(); + SearchResultProto.newBuilder() + .addResults( + SearchResultProto.ResultProto.newBuilder() + .setDocument(documentProto) + .setSnippet(snippetProto)) + .build(); // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = @@ -218,27 +280,25 @@ public class SnippetTest { Collections.singletonList(PACKAGE_NAME), Collections.singletonList(DATABASE_NAME), SCHEMA_MAP); - for (SearchResult result : searchResultPage.getResults()) { - - SearchResult.MatchInfo match1 = result.getMatches().get(0); - assertThat(match1.getPropertyPath()).isEqualTo("sender.name"); - assertThat(match1.getFullText()).isEqualTo("Test Name Jr."); - assertThat(match1.getExactMatchRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 4)); - assertThat(match1.getExactMatch()).isEqualTo("Test"); - assertThat(match1.getSnippetRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9)); - assertThat(match1.getSnippet()).isEqualTo("Test Name"); - - SearchResult.MatchInfo match2 = result.getMatches().get(1); - assertThat(match2.getPropertyPath()).isEqualTo("sender.email"); - assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com"); - assertThat(match2.getExactMatchRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20)); - assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com"); - assertThat(match2.getSnippetRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20)); - assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com"); - } + assertThat(searchResultPage.getResults()).hasSize(1); + SearchResult.MatchInfo match1 = searchResultPage.getResults().get(0).getMatches().get(0); + assertThat(match1.getPropertyPath()).isEqualTo("sender.name"); + assertThat(match1.getFullText()).isEqualTo("Test Name Jr."); + assertThat(match1.getExactMatchRange()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 4)); + assertThat(match1.getExactMatch()).isEqualTo("Test"); + assertThat(match1.getSnippetRange()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9)); + assertThat(match1.getSnippet()).isEqualTo("Test Name"); + + SearchResult.MatchInfo match2 = searchResultPage.getResults().get(0).getMatches().get(1); + assertThat(match2.getPropertyPath()).isEqualTo("sender.email[1]"); + assertThat(match2.getFullText()).isEqualTo("TestNameJr2@gmail.com"); + assertThat(match2.getExactMatchRange()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 21)); + assertThat(match2.getExactMatch()).isEqualTo("TestNameJr2@gmail.com"); + assertThat(match2.getSnippetRange()) + .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 21)); + assertThat(match2.getSnippet()).isEqualTo("TestNameJr2@gmail.com"); } } diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index b9f70da60de2..358120664cea 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -704,36 +704,36 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { final String key = getKey(userId, databaseName); Map<String, GenericDocument> docMap = mDocumentMap.get(key); for (GenericDocument doc : docs) { - builder.setSuccess(doc.getUri(), null); + builder.setSuccess(doc.getId(), null); if (docMap == null) { docMap = new ArrayMap<>(1); mDocumentMap.put(key, docMap); } - docMap.put(doc.getUri(), doc); + docMap.put(doc.getId(), doc); } callback.onResult(builder.build()); } @Override public void getDocuments(String packageName, String databaseName, String namespace, - List<String> uris, Map<String, List<String>> typePropertyPaths, int userId, + List<String> ids, Map<String, List<String>> typePropertyPaths, int userId, IAppSearchBatchResultCallback callback) throws RemoteException { final AppSearchBatchResult.Builder<String, Bundle> builder = new AppSearchBatchResult.Builder<>(); final String key = getKey(userId, databaseName); if (!mDocumentMap.containsKey(key)) { - for (String uri : uris) { - builder.setFailure(uri, AppSearchResult.RESULT_NOT_FOUND, - key + " not found when getting: " + uri); + for (String id : ids) { + builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND, + key + " not found when getting: " + id); } } else { final Map<String, GenericDocument> docs = mDocumentMap.get(key); - for (String uri : uris) { - if (docs.containsKey(uri)) { - builder.setSuccess(uri, docs.get(uri).getBundle()); + for (String id : ids) { + if (docs.containsKey(id)) { + builder.setSuccess(id, docs.get(id).getBundle()); } else { - builder.setFailure(uri, AppSearchResult.RESULT_NOT_FOUND, - "shortcut not found: " + uri); + builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND, + "shortcut not found: " + id); } } } @@ -805,33 +805,33 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { @Override public void reportUsage(String packageName, String databaseName, String namespace, - String uri, long usageTimeMillis, boolean systemUsage, int userId, + String documentId, long usageTimestampMillis, boolean systemUsage, int userId, IAppSearchResultCallback callback) throws RemoteException { ignore(callback); } @Override - public void removeByUri(String packageName, String databaseName, String namespace, - List<String> uris, int userId, IAppSearchBatchResultCallback callback) + public void removeByDocumentId(String packageName, String databaseName, String namespace, + List<String> ids, int userId, IAppSearchBatchResultCallback callback) throws RemoteException { final AppSearchBatchResult.Builder<String, Void> builder = new AppSearchBatchResult.Builder<>(); final String key = getKey(userId, databaseName); if (!mDocumentMap.containsKey(key)) { - for (String uri : uris) { - builder.setFailure(uri, AppSearchResult.RESULT_NOT_FOUND, - "package " + key + " not found when removing " + uri); + for (String id : ids) { + builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND, + "package " + key + " not found when removing " + id); } } else { final Map<String, GenericDocument> docs = mDocumentMap.get(key); - for (String uri : uris) { - if (docs.containsKey(uri)) { - docs.remove(uri); - builder.setSuccess(uri, null); + for (String id : ids) { + if (docs.containsKey(id)) { + docs.remove(id); + builder.setSuccess(id, null); } else { - builder.setFailure(uri, AppSearchResult.RESULT_NOT_FOUND, - "shortcut not found when removing " + uri); + builder.setFailure(id, AppSearchResult.RESULT_NOT_FOUND, + "shortcut not found when removing " + id); } } } |