diff options
19 files changed, 384 insertions, 198 deletions
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt index ae9e7ff8de2f..ae32fba443f6 100644 --- a/apex/appsearch/framework/api/current.txt +++ b/apex/appsearch/framework/api/current.txt @@ -100,6 +100,7 @@ package android.app.appsearch { method public long getCreationTimestampMillis(); method public static int getMaxIndexedProperties(); method @NonNull public String getNamespace(); + method @Nullable public Object getProperty(@NonNull String); method public boolean getPropertyBoolean(@NonNull String); method @Nullable public boolean[] getPropertyBooleanArray(@NonNull String); method @Nullable public byte[] getPropertyBytes(@NonNull String); @@ -148,6 +149,12 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.GetByUriRequest.Builder setNamespace(@NonNull String); } + public class PackageIdentifier { + ctor public PackageIdentifier(@NonNull String, @NonNull byte[]); + method @NonNull public String getPackageName(); + method @NonNull public byte[] getSha256Certificate(); + } + public final class PutDocumentsRequest { method @NonNull public java.util.List<android.app.appsearch.GenericDocument> getDocuments(); } @@ -233,6 +240,8 @@ package android.app.appsearch { public final class SetSchemaRequest { method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas(); + method @NonNull public java.util.Set<java.lang.String> getSchemasNotVisibleToSystemUi(); + method @NonNull public java.util.Map<java.lang.String,java.util.Set<android.app.appsearch.PackageIdentifier>> getSchemasVisibleToPackages(); method public boolean isForceOverride(); } @@ -242,6 +251,17 @@ package android.app.appsearch { method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchema(@NonNull java.util.Collection<android.app.appsearch.AppSearchSchema>); method @NonNull public android.app.appsearch.SetSchemaRequest build(); method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean); + method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(@NonNull String, boolean, @NonNull android.app.appsearch.PackageIdentifier); + method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(@NonNull String, boolean); + } + +} + +package android.app.appsearch.exceptions { + + public class AppSearchException extends java.lang.Exception { + method public int getResultCode(); + method @NonNull public <T> android.app.appsearch.AppSearchResult<T> toAppSearchResult(); } } 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 85207f7ed9d7..11e7fab2b7d9 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java @@ -20,7 +20,6 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; -import android.app.appsearch.exceptions.AppSearchException; import android.app.appsearch.util.BundleUtil; import android.os.Bundle; import android.util.Log; @@ -92,9 +91,7 @@ public class GenericDocument { /** Contains {@link GenericDocument} basic information (uri, schemaType etc). */ @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 keys */ @NonNull private final Bundle mProperties; @NonNull private final String mUri; @@ -202,6 +199,24 @@ public class GenericDocument { } /** + * Retrieves the property value with the given key as {@link Object}. + * + * @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. + */ + @Nullable + public Object getProperty(@NonNull String key) { + Preconditions.checkNotNull(key); + Object property = mProperties.get(key); + if (property instanceof ArrayList) { + return getPropertyBytesArray(key); + } else if (property instanceof Bundle[]) { + return getPropertyDocumentArray(key); + } + return property; + } + + /** * Retrieves a {@link String} value by key. * * @param key The key to look for. diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java b/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java index 8b20c0903ce1..43be442bd4dc 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java @@ -23,10 +23,7 @@ import com.android.internal.util.Preconditions; import java.util.Arrays; import java.util.Objects; -/** - * This class represents a uniquely identifiable package. - * @hide - */ +/** This class represents a uniquely identifiable package. */ public class PackageIdentifier { private final String mPackageName; private final byte[] mSha256Certificate; diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java index 1c360a65a041..b9503eed153c 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java @@ -18,7 +18,6 @@ package android.app.appsearch; import android.annotation.NonNull; import android.annotation.SuppressLint; -import android.app.appsearch.exceptions.AppSearchException; import com.android.internal.util.Preconditions; 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 5ffa7c94087c..eb0b7324a117 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java @@ -49,31 +49,20 @@ public final class SearchResult { /** @hide */ public static final String MATCHES_FIELD = "matches"; - @NonNull private final Bundle mBundle; + /** @hide */ + public static final String PACKAGE_NAME_FIELD = "packageName"; - @NonNull private final Bundle mDocumentBundle; + @NonNull private final Bundle mBundle; /** Cache of the inflated document. Comes from inflating mDocumentBundle at first use. */ @Nullable private GenericDocument mDocument; - /** - * Contains a list of MatchInfo bundles that matched the request. - * - * <p>Only populated when requested in both {@link SearchSpec.Builder#setSnippetCount} and - * {@link SearchSpec.Builder#setSnippetCountPerProperty}. - * - * @see #getMatches() - */ - @NonNull private final List<Bundle> mMatchBundles; - /** Cache of the inflated matches. Comes from inflating mMatchBundles at first use. */ @Nullable private List<MatchInfo> mMatches; /** @hide */ public SearchResult(@NonNull Bundle bundle) { mBundle = Preconditions.checkNotNull(bundle); - mDocumentBundle = Preconditions.checkNotNull(bundle.getBundle(DOCUMENT_FIELD)); - mMatchBundles = Preconditions.checkNotNull(bundle.getParcelableArrayList(MATCHES_FIELD)); } /** @hide */ @@ -90,7 +79,9 @@ public final class SearchResult { @NonNull public GenericDocument getDocument() { if (mDocument == null) { - mDocument = new GenericDocument(mDocumentBundle); + mDocument = + new GenericDocument( + Preconditions.checkNotNull(mBundle.getBundle(DOCUMENT_FIELD))); } return mDocument; } @@ -106,9 +97,11 @@ public final class SearchResult { @NonNull public List<MatchInfo> getMatches() { if (mMatches == null) { - mMatches = new ArrayList<>(mMatchBundles.size()); - for (int i = 0; i < mMatchBundles.size(); i++) { - MatchInfo matchInfo = new MatchInfo(getDocument(), mMatchBundles.get(i)); + List<Bundle> matchBundles = + Preconditions.checkNotNull(mBundle.getParcelableArrayList(MATCHES_FIELD)); + mMatches = new ArrayList<>(matchBundles.size()); + for (int i = 0; i < matchBundles.size(); i++) { + MatchInfo matchInfo = new MatchInfo(getDocument(), matchBundles.get(i)); mMatches.add(matchInfo); } } @@ -116,6 +109,17 @@ public final class SearchResult { } /** + * Contains the package name that stored the {@link GenericDocument}. + * + * @return Package name that stored the document + * @hide + */ + @NonNull + public String getPackageName() { + return Preconditions.checkNotNull(mBundle.getString(PACKAGE_NAME_FIELD)); + } + + /** * This class represents a match objects for any Snippets that might be present in {@link * SearchResults} from query. Using this class user can get the full text, exact matches and * Snippets of document content for a given match. 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 400b6303d12e..c3f0d8ab53e3 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java @@ -19,8 +19,6 @@ package android.app.appsearch; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; -import android.annotation.SuppressLint; -import android.app.appsearch.exceptions.AppSearchException; import android.app.appsearch.exceptions.IllegalSearchSpecException; import android.os.Bundle; import android.util.ArrayMap; 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 ad3ee0587f61..e9c4cb4e9e34 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java @@ -59,7 +59,6 @@ public final class SetSchemaRequest { /** * Returns the set of schema types that have opted out of being visible on system UI surfaces. - * @hide */ @NonNull public Set<String> getSchemasNotVisibleToSystemUi() { @@ -72,7 +71,6 @@ public final class SetSchemaRequest { * certificate. * * <p>This method is inefficient to call repeatedly. - * @hide */ @NonNull public Map<String, Set<PackageIdentifier>> getSchemasVisibleToPackages() { @@ -141,7 +139,6 @@ public final class SetSchemaRequest { * * @param schemaType The schema type to set visibility on. * @param visible Whether the {@code schemaType} will be visible or not. - * @hide */ // Merged list available from getSchemasNotVisibleToSystemUi @SuppressLint("MissingGetterMatchingBuilder") @@ -165,7 +162,6 @@ public final class SetSchemaRequest { * @param schemaType The schema type to set visibility on. * @param visible Whether the {@code schemaType} will be visible or not. * @param packageIdentifier Represents the package that will be granted visibility. - * @hide */ // Merged list available from getSchemasVisibleToPackages @SuppressLint("MissingGetterMatchingBuilder") diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java index 704f180bffc4..b1a33a478a47 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java @@ -25,8 +25,6 @@ import android.app.appsearch.AppSearchResult; * * <p>These exceptions can be converted into a failed {@link AppSearchResult} for propagating to the * client. - * - * @hide */ public class AppSearchException extends Exception { private final @AppSearchResult.ResultCode int mResultCode; 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 47a81eb37c7d..a2126b19d31e 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 @@ -37,6 +37,7 @@ import com.android.server.appsearch.external.localstorage.converter.SearchResult import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter; import com.google.android.icing.IcingSearchEngine; +import com.google.android.icing.proto.DeleteByQueryResultProto; import com.google.android.icing.proto.DeleteResultProto; import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.GetAllNamespacesResultProto; @@ -609,7 +610,7 @@ public final class AppSearchImpl { SearchSpecProto searchSpecProto = SearchSpecToProtoConverter.toSearchSpecProto(searchSpec); SearchSpecProto.Builder searchSpecBuilder = searchSpecProto.toBuilder().setQuery(queryExpression); - DeleteResultProto deleteResultProto; + DeleteByQueryResultProto deleteResultProto; mReadWriteLock.writeLock().lock(); try { // Only rewrite SearchSpec for non empty prefixes. @@ -797,11 +798,27 @@ public final class AppSearchImpl { * Removes any prefixes from types and namespaces mentioned anywhere in {@code documentBuilder}. * * @param documentBuilder The document to mutate + * @return Prefix name that was removed from the document. + * @throws AppSearchException if there are unexpected database prefixing errors. */ + @NonNull @VisibleForTesting - static void removePrefixesFromDocument(@NonNull DocumentProto.Builder documentBuilder) + static String removePrefixesFromDocument(@NonNull DocumentProto.Builder documentBuilder) throws AppSearchException { // Rewrite the type name and namespace to remove the prefix. + String schemaPrefix = getPrefix(documentBuilder.getSchema()); + String namespacePrefix = getPrefix(documentBuilder.getNamespace()); + + if (!schemaPrefix.equals(namespacePrefix)) { + throw new AppSearchException( + AppSearchResult.RESULT_INTERNAL_ERROR, + "Found unexpected" + + " multiple prefix names in document: " + + schemaPrefix + + ", " + + namespacePrefix); + } + documentBuilder.setSchema(removePrefix(documentBuilder.getSchema())); documentBuilder.setNamespace(removePrefix(documentBuilder.getNamespace())); @@ -816,12 +833,22 @@ public final class AppSearchImpl { for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) { DocumentProto.Builder derivedDocumentBuilder = propertyBuilder.getDocumentValues(documentIdx).toBuilder(); - removePrefixesFromDocument(derivedDocumentBuilder); + String nestedPrefix = removePrefixesFromDocument(derivedDocumentBuilder); + if (!nestedPrefix.equals(schemaPrefix)) { + throw new AppSearchException( + AppSearchResult.RESULT_INTERNAL_ERROR, + "Found unexpected multiple prefix names in document: " + + schemaPrefix + + ", " + + nestedPrefix); + } propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder); } documentBuilder.setProperties(propertyIdx, propertyBuilder); } } + + return schemaPrefix; } /** @@ -929,6 +956,25 @@ public final class AppSearchImpl { return packageName + PACKAGE_DELIMITER + databaseName + DATABASE_DELIMITER; } + /** + * Returns the package name that's contained within the {@code prefix}. + * + * @param prefix Prefix string that contains the package name inside of it. The package name + * must be in the front of the string, and separated from the rest of the string by the + * {@link #PACKAGE_DELIMITER}. + * @return Valid package name. + */ + @NonNull + private static String getPackageName(@NonNull String prefix) { + int delimiterIndex = prefix.indexOf(PACKAGE_DELIMITER); + if (delimiterIndex == -1) { + // This should never happen if we construct our prefixes properly + Log.wtf(TAG, "Malformed prefix doesn't contain package name: " + prefix); + return ""; + } + return prefix.substring(0, delimiterIndex); + } + @NonNull private static String removePrefix(@NonNull String prefixedString) throws AppSearchException { // The prefix is made up of the package, then the database. So we only need to find the @@ -949,7 +995,7 @@ public final class AppSearchImpl { if (databaseDelimiterIndex == -1) { throw new AppSearchException( AppSearchResult.RESULT_UNKNOWN_ERROR, - "The databaseName prefixed value doesn't contains a valid database name."); + "The databaseName prefixed value doesn't contain a valid database name."); } // Add 1 to include the char size of the DATABASE_DELIMITER @@ -1034,20 +1080,24 @@ public final class AppSearchImpl { } /** Remove the rewritten schema types from any result documents. */ - private static SearchResultPage rewriteSearchResultProto( - @NonNull SearchResultProto searchResultProto) throws AppSearchException { + @NonNull + @VisibleForTesting + static SearchResultPage rewriteSearchResultProto(@NonNull SearchResultProto searchResultProto) + throws AppSearchException { + // Parallel array of package names for each document search result. + List<String> packageNames = new ArrayList<>(searchResultProto.getResultsCount()); + SearchResultProto.Builder resultsBuilder = searchResultProto.toBuilder(); for (int i = 0; i < searchResultProto.getResultsCount(); i++) { - if (searchResultProto.getResults(i).hasDocument()) { - SearchResultProto.ResultProto.Builder resultBuilder = - searchResultProto.getResults(i).toBuilder(); - DocumentProto.Builder documentBuilder = resultBuilder.getDocument().toBuilder(); - removePrefixesFromDocument(documentBuilder); - resultBuilder.setDocument(documentBuilder); - resultsBuilder.setResults(i, resultBuilder); - } + SearchResultProto.ResultProto.Builder resultBuilder = + searchResultProto.getResults(i).toBuilder(); + DocumentProto.Builder documentBuilder = resultBuilder.getDocument().toBuilder(); + String prefix = removePrefixesFromDocument(documentBuilder); + packageNames.add(getPackageName(prefix)); + resultBuilder.setDocument(documentBuilder); + resultsBuilder.setResults(i, resultBuilder); } - return SearchResultToProtoConverter.toSearchResultPage(resultsBuilder); + return SearchResultToProtoConverter.toSearchResultPage(resultsBuilder, packageNames); } @GuardedBy("mReadWriteLock") 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 5474cd04287c..a2386eccc256 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 @@ -54,40 +54,43 @@ public final class GenericDocumentToProtoConverter { for (int i = 0; i < keys.size(); i++) { String name = keys.get(i); PropertyProto.Builder propertyProto = PropertyProto.newBuilder().setName(name); - String[] stringValues = document.getPropertyStringArray(name); - long[] longValues = document.getPropertyLongArray(name); - double[] doubleValues = document.getPropertyDoubleArray(name); - boolean[] booleanValues = document.getPropertyBooleanArray(name); - byte[][] bytesValues = document.getPropertyBytesArray(name); - GenericDocument[] documentValues = document.getPropertyDocumentArray(name); - if (stringValues != null) { + Object property = document.getProperty(name); + if (property instanceof String[]) { + String[] stringValues = (String[]) property; for (int j = 0; j < stringValues.length; j++) { propertyProto.addStringValues(stringValues[j]); } - } else if (longValues != null) { + } else if (property instanceof long[]) { + long[] longValues = (long[]) property; for (int j = 0; j < longValues.length; j++) { propertyProto.addInt64Values(longValues[j]); } - } else if (doubleValues != null) { + } else if (property instanceof double[]) { + double[] doubleValues = (double[]) property; for (int j = 0; j < doubleValues.length; j++) { propertyProto.addDoubleValues(doubleValues[j]); } - } else if (booleanValues != null) { + } else if (property instanceof boolean[]) { + boolean[] booleanValues = (boolean[]) property; for (int j = 0; j < booleanValues.length; j++) { propertyProto.addBooleanValues(booleanValues[j]); } - } else if (bytesValues != null) { + } else if (property instanceof byte[][]) { + byte[][] bytesValues = (byte[][]) property; for (int j = 0; j < bytesValues.length; j++) { propertyProto.addBytesValues(ByteString.copyFrom(bytesValues[j])); } - } else if (documentValues != null) { + } else if (property instanceof GenericDocument[]) { + GenericDocument[] documentValues = (GenericDocument[]) property; for (int j = 0; j < documentValues.length; j++) { DocumentProto proto = toDocumentProto(documentValues[j]); propertyProto.addDocumentValues(proto); } } else { throw new IllegalStateException( - "Property \"" + name + "\" has unsupported value type"); + String.format( + "Property \"%s\" has unsupported value type %s", + name, property.getClass().toString())); } mProtoBuilder.addProperties(propertyProto); } 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 4d107a970abd..ccd567d1c945 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 @@ -22,12 +22,15 @@ import android.app.appsearch.SearchResult; import android.app.appsearch.SearchResultPage; import android.os.Bundle; +import com.android.internal.util.Preconditions; + import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.SearchResultProtoOrBuilder; import com.google.android.icing.proto.SnippetMatchProto; import com.google.android.icing.proto.SnippetProto; import java.util.ArrayList; +import java.util.List; /** * Translates a {@link SearchResultProto} into {@link SearchResult}s. @@ -37,27 +40,45 @@ import java.util.ArrayList; public class SearchResultToProtoConverter { private SearchResultToProtoConverter() {} - /** Translate a {@link SearchResultProto} into {@link SearchResultPage}. */ + /** + * Translate a {@link SearchResultProto} into {@link SearchResultPage}. + * + * @param proto The {@link SearchResultProto} containing results. + * @param packageNames A parallel array of package names. The package name at index 'i' of this + * list should be the package that indexed the document at index 'i' of proto.getResults(i). + * @return {@link SearchResultPage} of results. + */ @NonNull - public static SearchResultPage toSearchResultPage(@NonNull SearchResultProtoOrBuilder proto) { + public static SearchResultPage toSearchResultPage( + @NonNull SearchResultProtoOrBuilder proto, @NonNull List<String> packageNames) { + Preconditions.checkArgument( + proto.getResultsCount() == packageNames.size(), + "Size of " + "results does not match the number of package names."); Bundle bundle = new Bundle(); bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken()); ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount()); for (int i = 0; i < proto.getResultsCount(); i++) { - resultBundles.add(toSearchResultBundle(proto.getResults(i))); + resultBundles.add(toSearchResultBundle(proto.getResults(i), packageNames.get(i))); } bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles); return new SearchResultPage(bundle); } - /** Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. */ + /** + * Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. + * + * @param proto The proto to be converted. + * @param packageName The package name associated with the document in {@code proto}. + * @return A {@link SearchResult} bundle. + */ @NonNull private static Bundle toSearchResultBundle( - @NonNull SearchResultProto.ResultProtoOrBuilder proto) { + @NonNull SearchResultProto.ResultProtoOrBuilder proto, @NonNull String packageName) { Bundle bundle = new Bundle(); GenericDocument document = GenericDocumentToProtoConverter.toGenericDocument(proto.getDocument()); bundle.putBundle(SearchResult.DOCUMENT_FIELD, document.getBundle()); + bundle.putString(SearchResult.PACKAGE_NAME_FIELD, packageName); ArrayList<Bundle> matchList = new ArrayList<>(); if (proto.hasSnippet()) { diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index 2b1ec08e1d8e..73f64dc500ae 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -I596ad1269b4d3a4f26db67f5d970aeaa3bf94a9d +I8b7425b3f87153547d1c8f5b560be5a54c9be97e diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java index 9e22bf6ddbbf..6859747286a8 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java @@ -46,7 +46,7 @@ public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim { private final ExecutorService mExecutor; @NonNull - public static ListenableFuture<GlobalSearchSessionShimImpl> createGlobalSearchSession() { + public static ListenableFuture<GlobalSearchSessionShim> createGlobalSearchSession() { Context context = ApplicationProvider.getApplicationContext(); AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class); SettableFuture<AppSearchResult<GlobalSearchSession>> future = SettableFuture.create(); 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 8383df4dd0cd..e439c5ab3947 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 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.app.appsearch; import android.annotation.NonNull; @@ -26,7 +27,7 @@ import java.util.Set; * Represents a connection to an AppSearch storage system where {@link GenericDocument}s can be * placed and queried. * - * All implementations of this interface must be thread safe. + * <p>All implementations of this interface must be thread safe. */ public interface AppSearchSessionShim { @@ -37,41 +38,42 @@ public interface AppSearchSessionShim { * to {@link #setSchema}, if any, to determine how to treat existing documents. The following * types of schema modifications are always safe and are made without deleting any existing * documents: + * * <ul> - * <li>Addition of new types - * <li>Addition of new - * {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or - * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} properties to a - * type - * <li>Changing the cardinality of a data type to be less restrictive (e.g. changing an - * {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a - * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} property. + * <li>Addition of new types + * <li>Addition of new {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or + * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} properties to a + * type + * <li>Changing the cardinality of a data type to be less restrictive (e.g. changing an {@link + * AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link + * AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} property. * </ul> * * <p>The following types of schema changes are not backwards-compatible: + * * <ul> - * <li>Removal of an existing type - * <li>Removal of a property from a type - * <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property - * <li>For properties of {@code Document} type, changing the schema type of - * {@code Document}s of that property - * <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an - * {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a - * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property). - * <li>Adding a - * {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property. + * <li>Removal of an existing type + * <li>Removal of a property from a type + * <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property + * <li>For properties of {@code Document} type, changing the schema type of {@code Document}s + * of that property + * <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an {@link + * AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a {@link + * AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property). + * <li>Adding a {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property. * </ul> + * * <p>Supplying a schema with such changes will, by default, result in this call completing its - * future with an {@link androidx.appsearch.exceptions.AppSearchException} with a code of + * future with an {@link android.app.appsearch.exceptions.AppSearchException} with a code of * {@link AppSearchResult#RESULT_INVALID_SCHEMA} and a message describing the incompatibility. * In this case the previously set schema will remain active. * * <p>If you need to make non-backwards-compatible changes as described above, you can set the * {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. In this case, - * instead of completing its future with an - * {@link androidx.appsearch.exceptions.AppSearchException} with the - * {@link AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not - * compatible with the new schema will be deleted and the incompatible schema will be applied. + * instead of completing its future with an {@link + * android.app.appsearch.exceptions.AppSearchException} with the {@link + * AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not compatible + * with the new schema will be deleted and the incompatible schema will be applied. * * <p>It is a no-op to set the same schema as has been previously set; this is handled * efficiently. @@ -79,8 +81,8 @@ public interface AppSearchSessionShim { * <p>By default, documents are visible on platform surfaces. To opt out, call {@code * SetSchemaRequest.Builder#setPlatformSurfaceable} with {@code surfaceable} as false. Any * visibility settings apply only to the schemas that are included in the {@code request}. - * Visibility settings for a schema type do not apply or persist across - * {@link SetSchemaRequest}s. + * Visibility settings for a schema type do not apply or persist across {@link + * SetSchemaRequest}s. * * @param request The schema update request. * @return The pending result of performing this operation. @@ -107,10 +109,9 @@ public interface AppSearchSessionShim { * schema type previously registered via the {@link #setSchema} method. * * @param request {@link PutDocumentsRequest} containing documents to be indexed - * @return The pending result of performing this operation. The keys of the returned - * {@link AppSearchBatchResult} are the URIs of the input documents. The values are - * {@code null} if they were successfully indexed, or a failed {@link AppSearchResult} - * otherwise. + * @return The pending result of performing this operation. The keys of the returned {@link + * AppSearchBatchResult} are the URIs of the input documents. The values are {@code null} if + * they were successfully indexed, or a failed {@link AppSearchResult} otherwise. */ @NonNull ListenableFuture<AppSearchBatchResult<String, Void>> putDocuments( @@ -120,11 +121,11 @@ public interface AppSearchSessionShim { * Retrieves {@link GenericDocument}s by URI. * * @param request {@link GetByUriRequest} containing URIs to be retrieved. - * @return The pending result of performing this operation. The keys of the returned - * {@link AppSearchBatchResult} are the input URIs. The values are the returned - * {@link GenericDocument}s 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}. + * @return The pending result of performing this operation. The keys of the returned {@link + * AppSearchBatchResult} are the input URIs. The values are the returned {@link + * GenericDocument}s 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}. */ @NonNull ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByUri( @@ -134,42 +135,39 @@ public interface AppSearchSessionShim { * Searches a document based on a given query string. * * <p>Currently we support following features in the raw query format: + * * <ul> - * <li>AND - * <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and - * ‘cat’”). - * Example: hello world matches documents that have both ‘hello’ and ‘world’ - * <li>OR - * <p>OR joins (e.g. “match documents that have either the term ‘dog’ or - * ‘cat’”). - * Example: dog OR puppy - * <li>Exclusion - * <p>Exclude a term (e.g. “match documents that do - * not have the term ‘dog’”). - * Example: -dog excludes the term ‘dog’ - * <li>Grouping terms - * <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g. - * “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”). - * Example: (dog puppy) (cat kitten) two one group containing two terms. - * <li>Property restricts - * <p> Specifies which properties of a document to specifically match terms in (e.g. - * “match documents where the ‘subject’ property contains ‘important’”). - * Example: subject:important matches documents with the term ‘important’ in the - * ‘subject’ property - * <li>Schema type restricts - * <p>This is similar to property restricts, but allows for restricts on top-level document - * fields, such as schema_type. Clients should be able to limit their query to documents of - * a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”). - * Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents - * that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the - * ‘Video’ schema type. + * <li>AND + * <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and ‘cat’”). + * Example: hello world matches documents that have both ‘hello’ and ‘world’ + * <li>OR + * <p>OR joins (e.g. “match documents that have either the term ‘dog’ or ‘cat’”). Example: + * dog OR puppy + * <li>Exclusion + * <p>Exclude a term (e.g. “match documents that do not have the term ‘dog’”). Example: + * -dog excludes the term ‘dog’ + * <li>Grouping terms + * <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g. + * “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”). + * Example: (dog puppy) (cat kitten) two one group containing two terms. + * <li>Property restricts + * <p>Specifies which properties of a document to specifically match terms in (e.g. “match + * documents where the ‘subject’ property contains ‘important’”). Example: + * subject:important matches documents with the term ‘important’ in the ‘subject’ property + * <li>Schema type restricts + * <p>This is similar to property restricts, but allows for restricts on top-level + * document fields, such as schema_type. Clients should be able to limit their query to + * documents of a certain schema_type (e.g. “match documents that are of the ‘Email’ + * schema_type”). Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will + * match documents that contain the query term ‘dog’ and are of either the ‘Email’ schema + * type or the ‘Video’ schema type. * </ul> * - * <p> This method is lightweight. The heavy work will be done in - * {@link SearchResults#getNextPage()}. + * <p>This method is lightweight. The heavy work will be done in {@link + * SearchResultsShim#getNextPage()}. * * @param queryExpression Query String to search. - * @param searchSpec Spec for setting filters, raw query etc. + * @param searchSpec Spec for setting filters, raw query etc. * @return The search result of performing this operation. */ @NonNull @@ -179,11 +177,10 @@ public interface AppSearchSessionShim { * Removes {@link GenericDocument}s from the index by URI. * * @param request Request containing URIs to be removed. - * @return 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}. + * @return 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}. */ @NonNull ListenableFuture<AppSearchBatchResult<String, Void>> removeByUri( @@ -191,18 +188,18 @@ public interface AppSearchSessionShim { /** * Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they - * match the {@code queryExpression} in given namespaces and schemaTypes which is set via - * {@link SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchemaType}. + * match the {@code queryExpression} in given namespaces and schemaTypes which is set via {@link + * SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchemaType}. * - * <p> An empty {@code queryExpression} matches all documents. + * <p>An empty {@code queryExpression} matches all documents. * - * <p> An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in - * the current database. + * <p>An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in the + * current database. * * @param queryExpression Query String to search. - * @param searchSpec Spec containing schemaTypes, namespaces and query expression - * indicates how document will be removed. All specific about how to - * scoring, ordering, snippeting and resulting will be ignored. + * @param searchSpec Spec containing schemaTypes, namespaces and query expression indicates how + * document will be removed. All specific about how to scoring, ordering, snippeting and + * resulting will be ignored. * @return The pending result of performing this operation. */ @NonNull 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 33dc3795325b..2d09247dfbc9 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 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.app.appsearch; import android.annotation.NonNull; @@ -27,42 +28,39 @@ public interface GlobalSearchSessionShim { * Searches across all documents in the storage based on a given query string. * * <p>Currently we support following features in the raw query format: + * * <ul> - * <li>AND - * <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and - * ‘cat’”). - * Example: hello world matches documents that have both ‘hello’ and ‘world’ - * <li>OR - * <p>OR joins (e.g. “match documents that have either the term ‘dog’ or - * ‘cat’”). - * Example: dog OR puppy - * <li>Exclusion - * <p>Exclude a term (e.g. “match documents that do - * not have the term ‘dog’”). - * Example: -dog excludes the term ‘dog’ - * <li>Grouping terms - * <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g. - * “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”). - * Example: (dog puppy) (cat kitten) two one group containing two terms. - * <li>Property restricts - * <p> Specifies which properties of a document to specifically match terms in (e.g. - * “match documents where the ‘subject’ property contains ‘important’”). - * Example: subject:important matches documents with the term ‘important’ in the - * ‘subject’ property - * <li>Schema type restricts - * <p>This is similar to property restricts, but allows for restricts on top-level document - * fields, such as schema_type. Clients should be able to limit their query to documents of - * a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”). - * Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents - * that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the - * ‘Video’ schema type. + * <li>AND + * <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and ‘cat’”). + * Example: hello world matches documents that have both ‘hello’ and ‘world’ + * <li>OR + * <p>OR joins (e.g. “match documents that have either the term ‘dog’ or ‘cat’”). Example: + * dog OR puppy + * <li>Exclusion + * <p>Exclude a term (e.g. “match documents that do not have the term ‘dog’”). Example: + * -dog excludes the term ‘dog’ + * <li>Grouping terms + * <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g. + * “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”). + * Example: (dog puppy) (cat kitten) two one group containing two terms. + * <li>Property restricts + * <p>Specifies which properties of a document to specifically match terms in (e.g. “match + * documents where the ‘subject’ property contains ‘important’”). Example: + * subject:important matches documents with the term ‘important’ in the ‘subject’ property + * <li>Schema type restricts + * <p>This is similar to property restricts, but allows for restricts on top-level + * document fields, such as schema_type. Clients should be able to limit their query to + * documents of a certain schema_type (e.g. “match documents that are of the ‘Email’ + * schema_type”). Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will + * match documents that contain the query term ‘dog’ and are of either the ‘Email’ schema + * type or the ‘Video’ schema type. * </ul> * - * <p> This method is lightweight. The heavy work will be done in - * {@link SearchResults#getNextPage}. + * <p>This method is lightweight. The heavy work will be done in {@link + * SearchResultsShim#getNextPage()}. * * @param queryExpression Query String to search. - * @param searchSpec Spec for setting filters, raw query etc. + * @param searchSpec Spec for setting filters, raw query etc. * @return The search result of performing this operation. */ @NonNull diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java index f387a1740386..328c65ca2727 100644 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java +++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * 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. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package android.app.appsearch; import android.annotation.NonNull; @@ -23,10 +24,10 @@ import java.io.Closeable; import java.util.List; /** - * SearchResults are a returned object from a query API. + * SearchResultsShim are a returned object from a query API. * - * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets - * based on request. + * <p>Each {@link SearchResult} contains a document and may contain other fields like snippets based + * on request. * * <p>Should close this object after finish fetching results. * @@ -36,11 +37,10 @@ public interface SearchResultsShim extends Closeable { /** * Gets a whole page of {@link SearchResult}s. * - * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an - * empty list. + * <p>Re-call this method to get next page of {@link SearchResult}, until it returns an empty + * list. * - * <p>The page size is set by - * {@link android.app.appsearch.SearchSpec.Builder#setResultCountPerPage}. + * <p>The page size is set by {@link SearchSpec.Builder#setResultCountPerPage}. * * @return The pending result of performing this operation. */ diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java index 133efce58b70..aab922972c54 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java @@ -16,6 +16,7 @@ package android.app.appsearch; + import static com.google.common.truth.Truth.assertThat; import static org.testng.Assert.expectThrows; 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 f29b0594db8f..c6fde87f5953 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 @@ -22,10 +22,12 @@ import static org.testng.Assert.expectThrows; import android.app.appsearch.AppSearchSchema; import android.app.appsearch.GenericDocument; +import android.app.appsearch.SearchResult; import android.app.appsearch.SearchResultPage; import android.app.appsearch.SearchSpec; import android.app.appsearch.exceptions.AppSearchException; +import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter; import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter; import com.android.server.appsearch.proto.DocumentProto; import com.android.server.appsearch.proto.GetOptimizeInfoResultProto; @@ -33,6 +35,7 @@ import com.android.server.appsearch.proto.PropertyConfigProto; import com.android.server.appsearch.proto.PropertyProto; import com.android.server.appsearch.proto.SchemaProto; import com.android.server.appsearch.proto.SchemaTypeConfigProto; +import com.android.server.appsearch.proto.SearchResultProto; import com.android.server.appsearch.proto.SearchSpecProto; import com.android.server.appsearch.proto.StringIndexingConfig; import com.android.server.appsearch.proto.TermMatchType; @@ -316,14 +319,14 @@ public class AppSearchImplTest { DocumentProto insideDocument = DocumentProto.newBuilder() .setUri("inside-uri") - .setSchema("package$databaseName1/type") - .setNamespace("package$databaseName2/namespace") + .setSchema("package$databaseName/type") + .setNamespace("package$databaseName/namespace") .build(); DocumentProto documentProto = DocumentProto.newBuilder() .setUri("uri") - .setSchema("package$databaseName2/type") - .setNamespace("package$databaseName3/namespace") + .setSchema("package$databaseName/type") + .setNamespace("package$databaseName/namespace") .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) .build(); @@ -345,11 +348,56 @@ public class AppSearchImplTest { .build(); DocumentProto.Builder actualDocument = documentProto.toBuilder(); - mAppSearchImpl.removePrefixesFromDocument(actualDocument); + assertThat(mAppSearchImpl.removePrefixesFromDocument(actualDocument)) + .isEqualTo("package$databaseName/"); assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto); } @Test + public void testRemoveDatabasesFromDocumentThrowsException() throws Exception { + // Set two different database names in the document, which should never happen + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri("uri") + .setSchema("prefix1/type") + .setNamespace("prefix2/namespace") + .build(); + + DocumentProto.Builder actualDocument = documentProto.toBuilder(); + AppSearchException e = + expectThrows( + AppSearchException.class, + () -> mAppSearchImpl.removePrefixesFromDocument(actualDocument)); + assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names"); + } + + @Test + public void testNestedRemoveDatabasesFromDocumentThrowsException() throws Exception { + // Set two different database names in the outer and inner document, which should never + // happen. + DocumentProto insideDocument = + DocumentProto.newBuilder() + .setUri("inside-uri") + .setSchema("prefix1/type") + .setNamespace("prefix1/namespace") + .build(); + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri("uri") + .setSchema("prefix2/type") + .setNamespace("prefix2/namespace") + .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) + .build(); + + DocumentProto.Builder actualDocument = documentProto.toBuilder(); + AppSearchException e = + expectThrows( + AppSearchException.class, + () -> mAppSearchImpl.removePrefixesFromDocument(actualDocument)); + assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names"); + } + + @Test public void testOptimize() throws Exception { // Insert schema List<AppSearchSchema> schemas = @@ -865,4 +913,40 @@ public class AppSearchImplTest { AppSearchImpl.createPrefix("package", "database1"), AppSearchImpl.createPrefix("package", "database2")); } + + @Test + public void testRewriteSearchResultProto() throws Exception { + final String database = + "com.package.foo" + + AppSearchImpl.PACKAGE_DELIMITER + + "databaseName" + + AppSearchImpl.DATABASE_DELIMITER; + final String uri = "uri"; + final String namespace = database + "namespace"; + final String schemaType = database + "schema"; + + // Building the SearchResult received from query. + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri(uri) + .setNamespace(namespace) + .setSchema(schemaType) + .build(); + SearchResultProto.ResultProto resultProto = + SearchResultProto.ResultProto.newBuilder().setDocument(documentProto).build(); + SearchResultProto searchResultProto = + SearchResultProto.newBuilder().addResults(resultProto).build(); + + DocumentProto.Builder strippedDocumentProto = documentProto.toBuilder(); + AppSearchImpl.removePrefixesFromDocument(strippedDocumentProto); + SearchResultPage searchResultPage = + AppSearchImpl.rewriteSearchResultProto(searchResultProto); + for (SearchResult result : searchResultPage.getResults()) { + assertThat(result.getPackageName()).isEqualTo("com.package.foo"); + assertThat(result.getDocument()) + .isEqualTo( + GenericDocumentToProtoConverter.toGenericDocument( + strippedDocumentProto.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 7c68c6be4883..a3f0f6bf280e 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 @@ -29,6 +29,8 @@ import com.android.server.appsearch.proto.SnippetProto; import org.junit.Test; +import java.util.Collections; + public class SnippetTest { // TODO(tytytyww): Add tests for Double and Long Snippets. @@ -83,7 +85,8 @@ public class SnippetTest { // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = - SearchResultToProtoConverter.toSearchResultPage(searchResultProto); + SearchResultToProtoConverter.toSearchResultPage( + searchResultProto, Collections.singletonList("packageName")); for (SearchResult result : searchResultPage.getResults()) { SearchResult.MatchInfo match = result.getMatches().get(0); assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString); @@ -131,7 +134,8 @@ public class SnippetTest { SearchResultProto.newBuilder().addResults(resultProto).build(); SearchResultPage searchResultPage = - SearchResultToProtoConverter.toSearchResultPage(searchResultProto); + SearchResultToProtoConverter.toSearchResultPage( + searchResultProto, Collections.singletonList("packageName")); for (SearchResult result : searchResultPage.getResults()) { assertThat(result.getMatches()).isEmpty(); } @@ -196,7 +200,8 @@ public class SnippetTest { // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = - SearchResultToProtoConverter.toSearchResultPage(searchResultProto); + SearchResultToProtoConverter.toSearchResultPage( + searchResultProto, Collections.singletonList("packageName")); for (SearchResult result : searchResultPage.getResults()) { SearchResult.MatchInfo match1 = result.getMatches().get(0); |