diff options
39 files changed, 1158 insertions, 517 deletions
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java index b66837d1f679..b06e21516cac 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java @@ -37,6 +37,8 @@ import java.util.Objects; * @param <ValueType> The type of result object for successful calls. */ public final class AppSearchResult<ValueType> implements Parcelable { + private static final String TAG = "AppSearchResult"; + /** * Result codes from {@link AppSearchSession} methods. * @hide @@ -246,14 +248,22 @@ public final class AppSearchResult<ValueType> implements Parcelable { @NonNull public static <ValueType> AppSearchResult<ValueType> throwableToFailedResult( @NonNull Throwable t) { - Log.d("AppSearchResult", "Converting throwable to failed result.", t); + // Log for traceability. NOT_FOUND is logged at VERBOSE because this error can occur during + // the regular operation of the system (b/183550974). Everything else is logged at DEBUG. + if (t instanceof AppSearchException + && ((AppSearchException) t).getResultCode() == RESULT_NOT_FOUND) { + Log.v(TAG, "Converting throwable to failed result: " + t); + } else { + Log.d(TAG, "Converting throwable to failed result.", t); + } if (t instanceof AppSearchException) { return ((AppSearchException) t).toAppSearchResult(); } + String exceptionClass = t.getClass().getSimpleName(); @AppSearchResult.ResultCode int resultCode; - if (t instanceof IllegalStateException) { + if (t instanceof IllegalStateException || t instanceof NullPointerException) { resultCode = AppSearchResult.RESULT_INTERNAL_ERROR; } else if (t instanceof IllegalArgumentException) { resultCode = AppSearchResult.RESULT_INVALID_ARGUMENT; @@ -262,6 +272,6 @@ public final class AppSearchResult<ValueType> implements Parcelable { } else { resultCode = AppSearchResult.RESULT_UNKNOWN_ERROR; } - return AppSearchResult.newFailedResult(resultCode, t.getMessage()); + return AppSearchResult.newFailedResult(resultCode, exceptionClass + ": " + t.getMessage()); } } 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 a8048dc5a4c4..2368bdb7466d 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java @@ -51,7 +51,7 @@ public final class AppSearchSchema { /** @hide */ public AppSearchSchema(@NonNull Bundle bundle) { - Preconditions.checkNotNull(bundle); + Objects.requireNonNull(bundle); mBundle = bundle; } @@ -125,7 +125,7 @@ public final class AppSearchSchema { /** Creates a new {@link AppSearchSchema.Builder}. */ public Builder(@NonNull String schemaType) { - Preconditions.checkNotNull(schemaType); + Objects.requireNonNull(schemaType); mSchemaType = schemaType; } @@ -133,7 +133,7 @@ public final class AppSearchSchema { @NonNull public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(propertyConfig); + Objects.requireNonNull(propertyConfig); String name = propertyConfig.getName(); if (!mPropertyNames.add(name)) { throw new IllegalSchemaException("Property defined more than once: " + name); @@ -246,7 +246,7 @@ public final class AppSearchSchema { @Nullable private Integer mHashCode; PropertyConfig(@NonNull Bundle bundle) { - mBundle = Preconditions.checkNotNull(bundle); + mBundle = Objects.requireNonNull(bundle); } @Override @@ -712,7 +712,7 @@ public final class AppSearchSchema { /** Returns the logical schema-type of the contents of this document property. */ @NonNull public String getSchemaType() { - return Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD)); + return Objects.requireNonNull(mBundle.getString(SCHEMA_TYPE_FIELD)); } /** @@ -755,7 +755,7 @@ public final class AppSearchSchema { @NonNull public DocumentPropertyConfig.Builder setSchemaType(@NonNull String schemaType) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(schemaType); + Objects.requireNonNull(schemaType); mBundle.putString(SCHEMA_TYPE_FIELD, schemaType); return this; } 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 8c9d950abe25..e3b3a859981d 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java @@ -31,6 +31,7 @@ import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Objects; import java.util.Set; /** @@ -101,11 +102,11 @@ public class GenericDocument { * @hide */ public GenericDocument(@NonNull Bundle bundle) { - Preconditions.checkNotNull(bundle); + Objects.requireNonNull(bundle); mBundle = bundle; - mProperties = Preconditions.checkNotNull(bundle.getParcelable(PROPERTIES_FIELD)); - mUri = Preconditions.checkNotNull(mBundle.getString(URI_FIELD)); - mSchemaType = Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD)); + mProperties = Objects.requireNonNull(bundle.getParcelable(PROPERTIES_FIELD)); + mUri = Objects.requireNonNull(mBundle.getString(URI_FIELD)); + mSchemaType = Objects.requireNonNull(mBundle.getString(SCHEMA_TYPE_FIELD)); mCreationTimestampMillis = mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis()); } @@ -199,7 +200,7 @@ public class GenericDocument { */ @Nullable public Object getProperty(@NonNull String key) { - Preconditions.checkNotNull(key); + Objects.requireNonNull(key); Object property = mProperties.get(key); if (property instanceof ArrayList) { return getPropertyBytesArray(key); @@ -218,7 +219,7 @@ public class GenericDocument { */ @Nullable public String getPropertyString(@NonNull String key) { - Preconditions.checkNotNull(key); + Objects.requireNonNull(key); String[] propertyArray = getPropertyStringArray(key); if (propertyArray == null || propertyArray.length == 0) { return null; @@ -235,7 +236,7 @@ public class GenericDocument { * there is no such key or the value is of a different type. */ public long getPropertyLong(@NonNull String key) { - Preconditions.checkNotNull(key); + Objects.requireNonNull(key); long[] propertyArray = getPropertyLongArray(key); if (propertyArray == null || propertyArray.length == 0) { return 0; @@ -252,7 +253,7 @@ public class GenericDocument { * if there is no such key or the value is of a different type. */ public double getPropertyDouble(@NonNull String key) { - Preconditions.checkNotNull(key); + Objects.requireNonNull(key); double[] propertyArray = getPropertyDoubleArray(key); if (propertyArray == null || propertyArray.length == 0) { return 0.0; @@ -269,7 +270,7 @@ public class GenericDocument { * false} if there is no such key or the value is of a different type. */ public boolean getPropertyBoolean(@NonNull String key) { - Preconditions.checkNotNull(key); + Objects.requireNonNull(key); boolean[] propertyArray = getPropertyBooleanArray(key); if (propertyArray == null || propertyArray.length == 0) { return false; @@ -287,7 +288,7 @@ public class GenericDocument { */ @Nullable public byte[] getPropertyBytes(@NonNull String key) { - Preconditions.checkNotNull(key); + Objects.requireNonNull(key); byte[][] propertyArray = getPropertyBytesArray(key); if (propertyArray == null || propertyArray.length == 0) { return null; @@ -305,7 +306,7 @@ public class GenericDocument { */ @Nullable public GenericDocument getPropertyDocument(@NonNull String key) { - Preconditions.checkNotNull(key); + Objects.requireNonNull(key); GenericDocument[] propertyArray = getPropertyDocumentArray(key); if (propertyArray == null || propertyArray.length == 0) { return null; @@ -342,7 +343,7 @@ public class GenericDocument { */ @Nullable public String[] getPropertyStringArray(@NonNull String key) { - Preconditions.checkNotNull(key); + Objects.requireNonNull(key); return getAndCastPropertyArray(key, String[].class); } @@ -355,7 +356,7 @@ public class GenericDocument { */ @Nullable public long[] getPropertyLongArray(@NonNull String key) { - Preconditions.checkNotNull(key); + Objects.requireNonNull(key); return getAndCastPropertyArray(key, long[].class); } @@ -368,7 +369,7 @@ public class GenericDocument { */ @Nullable public double[] getPropertyDoubleArray(@NonNull String key) { - Preconditions.checkNotNull(key); + Objects.requireNonNull(key); return getAndCastPropertyArray(key, double[].class); } @@ -381,7 +382,7 @@ public class GenericDocument { */ @Nullable public boolean[] getPropertyBooleanArray(@NonNull String key) { - Preconditions.checkNotNull(key); + Objects.requireNonNull(key); return getAndCastPropertyArray(key, boolean[].class); } @@ -396,7 +397,7 @@ public class GenericDocument { @Nullable @SuppressWarnings("unchecked") public byte[][] getPropertyBytesArray(@NonNull String key) { - Preconditions.checkNotNull(key); + Objects.requireNonNull(key); ArrayList<Bundle> bundles = getAndCastPropertyArray(key, ArrayList.class); if (bundles == null || bundles.size() == 0) { return null; @@ -428,7 +429,7 @@ public class GenericDocument { @SuppressLint("ArrayReturn") @Nullable public GenericDocument[] getPropertyDocumentArray(@NonNull String key) { - Preconditions.checkNotNull(key); + Objects.requireNonNull(key); Parcelable[] bundles = getAndCastPropertyArray(key, Parcelable[].class); if (bundles == null || bundles.length == 0) { return null; @@ -591,9 +592,9 @@ public class GenericDocument { */ @SuppressWarnings("unchecked") public Builder(@NonNull String namespace, @NonNull String uri, @NonNull String schemaType) { - Preconditions.checkNotNull(namespace); - Preconditions.checkNotNull(uri); - Preconditions.checkNotNull(schemaType); + Objects.requireNonNull(namespace); + Objects.requireNonNull(uri); + Objects.requireNonNull(schemaType); mBuilderTypeInstance = (BuilderType) this; mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace); mBundle.putString(GenericDocument.URI_FIELD, uri); @@ -682,8 +683,8 @@ public class GenericDocument { @NonNull public BuilderType setPropertyString(@NonNull String key, @NonNull String... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(key); - Preconditions.checkNotNull(values); + Objects.requireNonNull(key); + Objects.requireNonNull(values); putInPropertyBundle(key, values); return mBuilderTypeInstance; } @@ -694,15 +695,14 @@ public class GenericDocument { * * @param key the key associated with the {@code values}. * @param values the {@code boolean} values of the property. - * @throws IllegalArgumentException if no values are provided or if values exceed maximum - * repeated property length. + * @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) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(key); - Preconditions.checkNotNull(values); + Objects.requireNonNull(key); + Objects.requireNonNull(values); putInPropertyBundle(key, values); return mBuilderTypeInstance; } @@ -712,15 +712,14 @@ public class GenericDocument { * * @param key the key associated with the {@code values}. * @param values the {@code long} values of the property. - * @throws IllegalArgumentException if no values are provided or if values exceed maximum - * repeated property length. + * @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) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(key); - Preconditions.checkNotNull(values); + Objects.requireNonNull(key); + Objects.requireNonNull(values); putInPropertyBundle(key, values); return mBuilderTypeInstance; } @@ -730,15 +729,14 @@ public class GenericDocument { * * @param key the key associated with the {@code values}. * @param values the {@code double} values of the property. - * @throws IllegalArgumentException if no values are provided or if values exceed maximum - * repeated property length. + * @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) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(key); - Preconditions.checkNotNull(values); + Objects.requireNonNull(key); + Objects.requireNonNull(values); putInPropertyBundle(key, values); return mBuilderTypeInstance; } @@ -755,8 +753,8 @@ public class GenericDocument { @NonNull public BuilderType setPropertyBytes(@NonNull String key, @NonNull byte[]... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(key); - Preconditions.checkNotNull(values); + Objects.requireNonNull(key); + Objects.requireNonNull(values); putInPropertyBundle(key, values); return mBuilderTypeInstance; } @@ -776,8 +774,8 @@ public class GenericDocument { public BuilderType setPropertyDocument( @NonNull String key, @NonNull GenericDocument... values) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(key); - Preconditions.checkNotNull(values); + Objects.requireNonNull(key); + Objects.requireNonNull(values); putInPropertyBundle(key, values); return mBuilderTypeInstance; } @@ -850,9 +848,7 @@ public class GenericDocument { } private static void validateRepeatedPropertyLength(@NonNull String key, int length) { - if (length == 0) { - throw new IllegalArgumentException("The input array is empty."); - } else if (length > MAX_REPEATED_PROPERTY_LENGTH) { + if (length > MAX_REPEATED_PROPERTY_LENGTH) { throw new IllegalArgumentException( "Repeated property \"" + key diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java index 1719e14b01e3..4dc3225bd179 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetByUriRequest.java @@ -28,6 +28,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -52,9 +53,9 @@ public final class GetByUriRequest { @NonNull String namespace, @NonNull Set<String> uris, @NonNull Map<String, List<String>> typePropertyPathsMap) { - mNamespace = Preconditions.checkNotNull(namespace); - mUris = Preconditions.checkNotNull(uris); - mTypePropertyPathsMap = Preconditions.checkNotNull(typePropertyPathsMap); + mNamespace = Objects.requireNonNull(namespace); + mUris = Objects.requireNonNull(uris); + mTypePropertyPathsMap = Objects.requireNonNull(typePropertyPathsMap); } /** Returns the namespace attached to the request. */ @@ -114,7 +115,7 @@ public final class GetByUriRequest { /** Creates a {@link GetByUriRequest.Builder} instance. */ public Builder(@NonNull String namespace) { - mNamespace = Preconditions.checkNotNull(namespace); + mNamespace = Objects.requireNonNull(namespace); } /** @@ -124,7 +125,7 @@ public final class GetByUriRequest { */ @NonNull public Builder addUris(@NonNull String... uris) { - Preconditions.checkNotNull(uris); + Objects.requireNonNull(uris); return addUris(Arrays.asList(uris)); } @@ -136,7 +137,7 @@ public final class GetByUriRequest { @NonNull public Builder addUris(@NonNull Collection<String> uris) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(uris); + Objects.requireNonNull(uris); mUris.addAll(uris); return this; } @@ -161,11 +162,11 @@ public final class GetByUriRequest { public Builder addProjection( @NonNull String schemaType, @NonNull Collection<String> propertyPaths) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(schemaType); - Preconditions.checkNotNull(propertyPaths); + Objects.requireNonNull(schemaType); + Objects.requireNonNull(propertyPaths); List<String> propertyPathsList = new ArrayList<>(propertyPaths.size()); for (String propertyPath : propertyPaths) { - Preconditions.checkNotNull(propertyPath); + Objects.requireNonNull(propertyPath); propertyPathsList.add(propertyPath); } mProjectionTypePropertyPaths.put(schemaType, propertyPathsList); 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 1f56ef3588b1..691ef4ff939f 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java @@ -24,6 +24,7 @@ import android.util.ArraySet; import com.android.internal.util.Preconditions; import java.util.ArrayList; +import java.util.Objects; import java.util.Set; /** The response class of {@link AppSearchSession#getSchema} */ @@ -34,7 +35,7 @@ public class GetSchemaResponse { private final Bundle mBundle; GetSchemaResponse(@NonNull Bundle bundle) { - mBundle = Preconditions.checkNotNull(bundle); + mBundle = Objects.requireNonNull(bundle); } /** 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 bfb9323047c8..4f63baeeb41c 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.app.appsearch.util.BundleUtil; import android.os.Bundle; -import com.android.internal.util.Preconditions; +import java.util.Objects; /** This class represents a uniquely identifiable package. */ public class PackageIdentifier { @@ -43,7 +43,7 @@ public class PackageIdentifier { /** @hide */ public PackageIdentifier(@NonNull Bundle bundle) { - mBundle = Preconditions.checkNotNull(bundle); + mBundle = Objects.requireNonNull(bundle); } /** @hide */ @@ -54,12 +54,12 @@ public class PackageIdentifier { @NonNull public String getPackageName() { - return Preconditions.checkNotNull(mBundle.getString(PACKAGE_NAME_FIELD)); + return Objects.requireNonNull(mBundle.getString(PACKAGE_NAME_FIELD)); } @NonNull public byte[] getSha256Certificate() { - return Preconditions.checkNotNull(mBundle.getByteArray(SHA256_CERTIFICATE_FIELD)); + return Objects.requireNonNull(mBundle.getByteArray(SHA256_CERTIFICATE_FIELD)); } @Override 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 01473be062bc..b49e0e8ca2cf 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java @@ -26,6 +26,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Objects; /** * Encapsulates a request to index documents into an {@link AppSearchSession} database. @@ -61,7 +62,7 @@ public final class PutDocumentsRequest { */ @NonNull public Builder addGenericDocuments(@NonNull GenericDocument... documents) { - Preconditions.checkNotNull(documents); + Objects.requireNonNull(documents); return addGenericDocuments(Arrays.asList(documents)); } @@ -74,7 +75,7 @@ public final class PutDocumentsRequest { public Builder addGenericDocuments( @NonNull Collection<? extends GenericDocument> documents) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(documents); + Objects.requireNonNull(documents); mDocuments.addAll(documents); return this; } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java index 8da68c0b4898..4dcad68d49be 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByUriRequest.java @@ -24,6 +24,7 @@ import com.android.internal.util.Preconditions; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Objects; import java.util.Set; /** @@ -65,7 +66,7 @@ public final class RemoveByUriRequest { /** Creates a {@link RemoveByUriRequest.Builder} instance. */ public Builder(@NonNull String namespace) { - mNamespace = Preconditions.checkNotNull(namespace); + mNamespace = Objects.requireNonNull(namespace); } /** @@ -75,7 +76,7 @@ public final class RemoveByUriRequest { */ @NonNull public Builder addUris(@NonNull String... uris) { - Preconditions.checkNotNull(uris); + Objects.requireNonNull(uris); return addUris(Arrays.asList(uris)); } @@ -87,7 +88,7 @@ public final class RemoveByUriRequest { @NonNull public Builder addUris(@NonNull Collection<String> uris) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(uris); + Objects.requireNonNull(uris); mUris.addAll(uris); return this; } 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 2e152f89465f..8aff3b41cbc7 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; import com.android.internal.util.Preconditions; +import java.util.Objects; + /** * A request to report usage of a document owned by another app from a system UI surface. * @@ -42,10 +44,10 @@ public final class ReportSystemUsageRequest { @NonNull String namespace, @NonNull String uri, long usageTimeMillis) { - mPackageName = Preconditions.checkNotNull(packageName); - mDatabase = Preconditions.checkNotNull(database); - mNamespace = Preconditions.checkNotNull(namespace); - mUri = Preconditions.checkNotNull(uri); + mPackageName = Objects.requireNonNull(packageName); + mDatabase = Objects.requireNonNull(database); + mNamespace = Objects.requireNonNull(namespace); + mUri = Objects.requireNonNull(uri); mUsageTimeMillis = usageTimeMillis; } @@ -95,9 +97,9 @@ public final class ReportSystemUsageRequest { /** Creates a {@link ReportSystemUsageRequest.Builder} instance. */ public Builder( @NonNull String packageName, @NonNull String database, @NonNull String namespace) { - mPackageName = Preconditions.checkNotNull(packageName); - mDatabase = Preconditions.checkNotNull(database); - mNamespace = Preconditions.checkNotNull(namespace); + mPackageName = Objects.requireNonNull(packageName); + mDatabase = Objects.requireNonNull(database); + mNamespace = Objects.requireNonNull(namespace); } /** @@ -110,7 +112,7 @@ public final class ReportSystemUsageRequest { @NonNull public ReportSystemUsageRequest.Builder setUri(@NonNull String uri) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(uri); + Objects.requireNonNull(uri); mUri = uri; return this; } @@ -142,7 +144,7 @@ public final class ReportSystemUsageRequest { @NonNull public ReportSystemUsageRequest build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(mUri, "ReportUsageRequest is missing a URI"); + Objects.requireNonNull(mUri, "ReportUsageRequest is missing a URI"); if (mUsageTimeMillis == null) { mUsageTimeMillis = System.currentTimeMillis(); } 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 646e73c24bd9..925bde92d69d 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; import com.android.internal.util.Preconditions; +import java.util.Objects; + /** * A request to report usage of a document. * @@ -33,8 +35,8 @@ public final class ReportUsageRequest { private final long mUsageTimeMillis; ReportUsageRequest(@NonNull String namespace, @NonNull String uri, long usageTimeMillis) { - mNamespace = Preconditions.checkNotNull(namespace); - mUri = Preconditions.checkNotNull(uri); + mNamespace = Objects.requireNonNull(namespace); + mUri = Objects.requireNonNull(uri); mUsageTimeMillis = usageTimeMillis; } @@ -69,7 +71,7 @@ public final class ReportUsageRequest { /** Creates a {@link ReportUsageRequest.Builder} instance. */ public Builder(@NonNull String namespace) { - mNamespace = Preconditions.checkNotNull(namespace); + mNamespace = Objects.requireNonNull(namespace); } /** @@ -82,7 +84,7 @@ public final class ReportUsageRequest { @NonNull public ReportUsageRequest.Builder setUri(@NonNull String uri) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(uri); + Objects.requireNonNull(uri); mUri = uri; return this; } @@ -114,7 +116,7 @@ public final class ReportUsageRequest { @NonNull public ReportUsageRequest build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(mUri, "ReportUsageRequest is missing a URI"); + Objects.requireNonNull(mUri, "ReportUsageRequest is missing a URI"); if (mUsageTimeMillis == null) { mUsageTimeMillis = System.currentTimeMillis(); } 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 55a228d94c10..432f838bc78c 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java @@ -59,7 +59,7 @@ public final class SearchResult { /** @hide */ public SearchResult(@NonNull Bundle bundle) { - mBundle = Preconditions.checkNotNull(bundle); + mBundle = Objects.requireNonNull(bundle); } /** @hide */ @@ -77,8 +77,7 @@ public final class SearchResult { public GenericDocument getGenericDocument() { if (mDocument == null) { mDocument = - new GenericDocument( - Preconditions.checkNotNull(mBundle.getBundle(DOCUMENT_FIELD))); + new GenericDocument(Objects.requireNonNull(mBundle.getBundle(DOCUMENT_FIELD))); } return mDocument; } @@ -95,7 +94,7 @@ public final class SearchResult { public List<MatchInfo> getMatches() { if (mMatches == null) { List<Bundle> matchBundles = - Preconditions.checkNotNull(mBundle.getParcelableArrayList(MATCHES_FIELD)); + Objects.requireNonNull(mBundle.getParcelableArrayList(MATCHES_FIELD)); mMatches = new ArrayList<>(matchBundles.size()); for (int i = 0; i < matchBundles.size(); i++) { MatchInfo matchInfo = new MatchInfo(matchBundles.get(i), getGenericDocument()); @@ -112,7 +111,7 @@ public final class SearchResult { */ @NonNull public String getPackageName() { - return Preconditions.checkNotNull(mBundle.getString(PACKAGE_NAME_FIELD)); + return Objects.requireNonNull(mBundle.getString(PACKAGE_NAME_FIELD)); } /** @@ -122,7 +121,7 @@ public final class SearchResult { */ @NonNull public String getDatabaseName() { - return Preconditions.checkNotNull(mBundle.getString(DATABASE_NAME_FIELD)); + return Objects.requireNonNull(mBundle.getString(DATABASE_NAME_FIELD)); } /** @@ -169,8 +168,8 @@ public final class SearchResult { * @param databaseName the database name the matched document belongs to. */ public Builder(@NonNull String packageName, @NonNull String databaseName) { - mBundle.putString(PACKAGE_NAME_FIELD, Preconditions.checkNotNull(packageName)); - mBundle.putString(DATABASE_NAME_FIELD, Preconditions.checkNotNull(databaseName)); + mBundle.putString(PACKAGE_NAME_FIELD, Objects.requireNonNull(packageName)); + mBundle.putString(DATABASE_NAME_FIELD, Objects.requireNonNull(databaseName)); } /** @@ -312,9 +311,9 @@ public final class SearchResult { @Nullable private MatchRange mWindowRange; MatchInfo(@NonNull Bundle bundle, @Nullable GenericDocument document) { - mBundle = Preconditions.checkNotNull(bundle); + mBundle = Objects.requireNonNull(bundle); mDocument = document; - mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD)); + mPropertyPath = Objects.requireNonNull(bundle.getString(PROPERTY_PATH_FIELD)); } /** @@ -449,7 +448,7 @@ public final class SearchResult { Preconditions.checkState(!mBuilt, "Builder has already been used"); mBundle.putString( SearchResult.MatchInfo.PROPERTY_PATH_FIELD, - Preconditions.checkNotNull(propertyPath)); + Objects.requireNonNull(propertyPath)); return this; } @@ -461,7 +460,7 @@ public final class SearchResult { @NonNull public Builder setExactMatchRange(@NonNull MatchRange matchRange) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(matchRange); + Objects.requireNonNull(matchRange); mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_LOWER_FIELD, matchRange.getStart()); mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_UPPER_FIELD, matchRange.getEnd()); return this; @@ -475,7 +474,7 @@ public final class SearchResult { @NonNull public Builder setSnippetRange(@NonNull MatchRange matchRange) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(matchRange); + Objects.requireNonNull(matchRange); mBundle.putInt(MatchInfo.SNIPPET_RANGE_LOWER_FIELD, matchRange.getStart()); mBundle.putInt(MatchInfo.SNIPPET_RANGE_UPPER_FIELD, matchRange.getEnd()); return this; diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java index dbd09d6bb91b..4853b5b03229 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java @@ -20,11 +20,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Bundle; -import com.android.internal.util.Preconditions; - import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; /** * This class represents a page of {@link SearchResult}s @@ -41,7 +40,7 @@ public class SearchResultPage { @NonNull private final Bundle mBundle; public SearchResultPage(@NonNull Bundle bundle) { - mBundle = Preconditions.checkNotNull(bundle); + mBundle = Objects.requireNonNull(bundle); mNextPageToken = mBundle.getLong(NEXT_PAGE_TOKEN_FIELD); } 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 19d94305b3da..d466bf1359d0 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java @@ -34,6 +34,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -176,7 +177,7 @@ public final class SearchSpec { /** @hide */ public SearchSpec(@NonNull Bundle bundle) { - Preconditions.checkNotNull(bundle); + Objects.requireNonNull(bundle); mBundle = bundle; } @@ -342,7 +343,7 @@ public final class SearchSpec { */ @NonNull public Builder addFilterSchemas(@NonNull String... schemas) { - Preconditions.checkNotNull(schemas); + Objects.requireNonNull(schemas); Preconditions.checkState(!mBuilt, "Builder has already been used"); return addFilterSchemas(Arrays.asList(schemas)); } @@ -355,7 +356,7 @@ public final class SearchSpec { */ @NonNull public Builder addFilterSchemas(@NonNull Collection<String> schemas) { - Preconditions.checkNotNull(schemas); + Objects.requireNonNull(schemas); Preconditions.checkState(!mBuilt, "Builder has already been used"); mSchemas.addAll(schemas); return this; @@ -369,7 +370,7 @@ public final class SearchSpec { */ @NonNull public Builder addFilterNamespaces(@NonNull String... namespaces) { - Preconditions.checkNotNull(namespaces); + Objects.requireNonNull(namespaces); Preconditions.checkState(!mBuilt, "Builder has already been used"); return addFilterNamespaces(Arrays.asList(namespaces)); } @@ -382,7 +383,7 @@ public final class SearchSpec { */ @NonNull public Builder addFilterNamespaces(@NonNull Collection<String> namespaces) { - Preconditions.checkNotNull(namespaces); + Objects.requireNonNull(namespaces); Preconditions.checkState(!mBuilt, "Builder has already been used"); mNamespaces.addAll(namespaces); return this; @@ -398,7 +399,7 @@ public final class SearchSpec { */ @NonNull public Builder addFilterPackageNames(@NonNull String... packageNames) { - Preconditions.checkNotNull(packageNames); + Objects.requireNonNull(packageNames); Preconditions.checkState(!mBuilt, "Builder has already been used"); return addFilterPackageNames(Arrays.asList(packageNames)); } @@ -413,7 +414,7 @@ public final class SearchSpec { */ @NonNull public Builder addFilterPackageNames(@NonNull Collection<String> packageNames) { - Preconditions.checkNotNull(packageNames); + Objects.requireNonNull(packageNames); Preconditions.checkState(!mBuilt, "Builder has already been used"); mPackageNames.addAll(packageNames); return this; @@ -586,11 +587,11 @@ public final class SearchSpec { public SearchSpec.Builder addProjection( @NonNull String schema, @NonNull Collection<String> propertyPaths) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(schema); - Preconditions.checkNotNull(propertyPaths); + Objects.requireNonNull(schema); + Objects.requireNonNull(propertyPaths); ArrayList<String> propertyPathsArrayList = new ArrayList<>(propertyPaths.size()); for (String propertyPath : propertyPaths) { - Preconditions.checkNotNull(propertyPath); + Objects.requireNonNull(propertyPath); propertyPathsArrayList.add(propertyPath); } mProjectionTypePropertyMasks.putStringArrayList(schema, propertyPathsArrayList); 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 5672bc7fdc13..8f7a0bf61f3e 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java @@ -28,6 +28,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Map; +import java.util.Objects; import java.util.Set; /** @@ -94,10 +95,10 @@ public final class SetSchemaRequest { @NonNull Map<String, Migrator> migrators, boolean forceOverride, int version) { - mSchemas = Preconditions.checkNotNull(schemas); - mSchemasNotDisplayedBySystem = Preconditions.checkNotNull(schemasNotDisplayedBySystem); - mSchemasVisibleToPackages = Preconditions.checkNotNull(schemasVisibleToPackages); - mMigrators = Preconditions.checkNotNull(migrators); + mSchemas = Objects.requireNonNull(schemas); + mSchemasNotDisplayedBySystem = Objects.requireNonNull(schemasNotDisplayedBySystem); + mSchemasVisibleToPackages = Objects.requireNonNull(schemasVisibleToPackages); + mMigrators = Objects.requireNonNull(migrators); mForceOverride = forceOverride; mVersion = version; } @@ -192,7 +193,7 @@ public final class SetSchemaRequest { */ @NonNull public Builder addSchemas(@NonNull AppSearchSchema... schemas) { - Preconditions.checkNotNull(schemas); + Objects.requireNonNull(schemas); return addSchemas(Arrays.asList(schemas)); } @@ -206,7 +207,7 @@ public final class SetSchemaRequest { @NonNull public Builder addSchemas(@NonNull Collection<AppSearchSchema> schemas) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - Preconditions.checkNotNull(schemas); + Objects.requireNonNull(schemas); mSchemas.addAll(schemas); return this; } @@ -231,7 +232,7 @@ public final class SetSchemaRequest { @NonNull public Builder setSchemaTypeDisplayedBySystem( @NonNull String schemaType, boolean displayed) { - Preconditions.checkNotNull(schemaType); + Objects.requireNonNull(schemaType); Preconditions.checkState(!mBuilt, "Builder has already been used"); if (displayed) { @@ -270,8 +271,8 @@ public final class SetSchemaRequest { @NonNull String schemaType, boolean visible, @NonNull PackageIdentifier packageIdentifier) { - Preconditions.checkNotNull(schemaType); - Preconditions.checkNotNull(packageIdentifier); + Objects.requireNonNull(schemaType); + Objects.requireNonNull(packageIdentifier); Preconditions.checkState(!mBuilt, "Builder has already been used"); Set<PackageIdentifier> packageIdentifiers = mSchemasVisibleToPackages.get(schemaType); @@ -321,8 +322,8 @@ public final class SetSchemaRequest { @NonNull @SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects. public Builder setMigrator(@NonNull String schemaType, @NonNull Migrator migrator) { - Preconditions.checkNotNull(schemaType); - Preconditions.checkNotNull(migrator); + Objects.requireNonNull(schemaType); + Objects.requireNonNull(migrator); mMigrators.put(schemaType, migrator); return this; } @@ -350,7 +351,7 @@ public final class SetSchemaRequest { */ @NonNull public Builder setMigrators(@NonNull Map<String, Migrator> migrators) { - Preconditions.checkNotNull(migrators); + Objects.requireNonNull(migrators); mMigrators.putAll(migrators); return this; } 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 d63e4372f61f..7be589f727ce 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; /** The response class of {@link AppSearchSession#setSchema} */ @@ -61,8 +62,8 @@ public class SetSchemaResponse { @Nullable private Set<String> mIncompatibleTypes; SetSchemaResponse(@NonNull Bundle bundle, @NonNull List<MigrationFailure> migrationFailures) { - mBundle = Preconditions.checkNotNull(bundle); - mMigrationFailures = Preconditions.checkNotNull(migrationFailures); + mBundle = Objects.requireNonNull(bundle); + mMigrationFailures = Objects.requireNonNull(migrationFailures); } SetSchemaResponse(@NonNull Bundle bundle) { @@ -103,7 +104,7 @@ public class SetSchemaResponse { if (mDeletedTypes == null) { mDeletedTypes = new ArraySet<>( - Preconditions.checkNotNull( + Objects.requireNonNull( mBundle.getStringArrayList(DELETED_TYPES_FIELD))); } return Collections.unmodifiableSet(mDeletedTypes); @@ -118,7 +119,7 @@ public class SetSchemaResponse { if (mMigratedTypes == null) { mMigratedTypes = new ArraySet<>( - Preconditions.checkNotNull( + Objects.requireNonNull( mBundle.getStringArrayList(MIGRATED_TYPES_FIELD))); } return Collections.unmodifiableSet(mMigratedTypes); @@ -139,7 +140,7 @@ public class SetSchemaResponse { if (mIncompatibleTypes == null) { mIncompatibleTypes = new ArraySet<>( - Preconditions.checkNotNull( + Objects.requireNonNull( mBundle.getStringArrayList(INCOMPATIBLE_TYPES_FIELD))); } return Collections.unmodifiableSet(mIncompatibleTypes); @@ -173,7 +174,7 @@ public class SetSchemaResponse { public Builder addMigrationFailures( @NonNull Collection<MigrationFailure> migrationFailures) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mMigrationFailures.addAll(Preconditions.checkNotNull(migrationFailures)); + mMigrationFailures.addAll(Objects.requireNonNull(migrationFailures)); return this; } @@ -181,7 +182,7 @@ public class SetSchemaResponse { @NonNull public Builder addMigrationFailure(@NonNull MigrationFailure migrationFailure) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mMigrationFailures.add(Preconditions.checkNotNull(migrationFailure)); + mMigrationFailures.add(Objects.requireNonNull(migrationFailure)); return this; } @@ -189,7 +190,7 @@ public class SetSchemaResponse { @NonNull public Builder addDeletedTypes(@NonNull Collection<String> deletedTypes) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mDeletedTypes.addAll(Preconditions.checkNotNull(deletedTypes)); + mDeletedTypes.addAll(Objects.requireNonNull(deletedTypes)); return this; } @@ -197,7 +198,7 @@ public class SetSchemaResponse { @NonNull public Builder addDeletedType(@NonNull String deletedType) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mDeletedTypes.add(Preconditions.checkNotNull(deletedType)); + mDeletedTypes.add(Objects.requireNonNull(deletedType)); return this; } @@ -205,7 +206,7 @@ public class SetSchemaResponse { @NonNull public Builder addIncompatibleTypes(@NonNull Collection<String> incompatibleTypes) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mIncompatibleTypes.addAll(Preconditions.checkNotNull(incompatibleTypes)); + mIncompatibleTypes.addAll(Objects.requireNonNull(incompatibleTypes)); return this; } @@ -213,7 +214,7 @@ public class SetSchemaResponse { @NonNull public Builder addIncompatibleType(@NonNull String incompatibleType) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mIncompatibleTypes.add(Preconditions.checkNotNull(incompatibleType)); + mIncompatibleTypes.add(Objects.requireNonNull(incompatibleType)); return this; } @@ -221,7 +222,7 @@ public class SetSchemaResponse { @NonNull public Builder addMigratedTypes(@NonNull Collection<String> migratedTypes) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mMigratedTypes.addAll(Preconditions.checkNotNull(migratedTypes)); + mMigratedTypes.addAll(Objects.requireNonNull(migratedTypes)); return this; } @@ -229,7 +230,7 @@ public class SetSchemaResponse { @NonNull public Builder addMigratedType(@NonNull String migratedType) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mMigratedTypes.add(Preconditions.checkNotNull(migratedType)); + mMigratedTypes.add(Objects.requireNonNull(migratedType)); return this; } @@ -318,7 +319,7 @@ public class SetSchemaResponse { @NonNull public Builder setSchemaType(@NonNull String schemaType) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mSchemaType = Preconditions.checkNotNull(schemaType); + mSchemaType = Objects.requireNonNull(schemaType); return this; } @@ -326,7 +327,7 @@ public class SetSchemaResponse { @NonNull public Builder setNamespace(@NonNull String namespace) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mNamespace = Preconditions.checkNotNull(namespace); + mNamespace = Objects.requireNonNull(namespace); return this; } @@ -334,7 +335,7 @@ public class SetSchemaResponse { @NonNull public Builder setUri(@NonNull String uri) { Preconditions.checkState(!mBuilt, "Builder has already been used"); - mUri = Preconditions.checkNotNull(uri); + mUri = Objects.requireNonNull(uri); return this; } @@ -343,7 +344,7 @@ public class SetSchemaResponse { public Builder setAppSearchResult(@NonNull AppSearchResult<Void> appSearchResult) { Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!appSearchResult.isSuccess(), "Input a success result"); - mFailureResult = Preconditions.checkNotNull(appSearchResult); + mFailureResult = Objects.requireNonNull(appSearchResult); return this; } diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/StorageInfo.java b/apex/appsearch/framework/java/external/android/app/appsearch/StorageInfo.java index dc04cf3068ce..502b9391893f 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/StorageInfo.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/StorageInfo.java @@ -21,6 +21,8 @@ import android.os.Bundle; import com.android.internal.util.Preconditions; +import java.util.Objects; + /** The response class of {@code AppSearchSession#getStorageInfo}. */ public class StorageInfo { @@ -31,7 +33,7 @@ public class StorageInfo { private final Bundle mBundle; StorageInfo(@NonNull Bundle bundle) { - mBundle = Preconditions.checkNotNull(bundle); + mBundle = Objects.requireNonNull(bundle); } /** diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java index 32d7e043e954..10e014bf9c9a 100644 --- a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java +++ b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java @@ -37,7 +37,11 @@ import java.util.Set; public final class SchemaMigrationUtil { private SchemaMigrationUtil() {} - /** Returns all active {@link Migrator}s that need to be triggered in this migration. */ + /** + * Returns all active {@link Migrator}s that need to be triggered in this migration. + * + * <p>{@link Migrator#shouldMigrate} returns {@code true} will make the {@link Migrator} active. + */ @NonNull public static Map<String, Migrator> getActiveMigrators( @NonNull Set<AppSearchSchema> existingSchemas, 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 509877e21862..11eb5ca7909b 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -64,6 +64,8 @@ import com.android.server.appsearch.external.localstorage.stats.CallStats; import com.android.server.appsearch.stats.LoggerInstanceManager; import com.android.server.appsearch.stats.PlatformLogger; +import com.google.android.icing.proto.PersistType; + import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; @@ -364,6 +366,8 @@ public class AppSearchManagerService extends SystemService { ++operationFailureCount; } } + // Now that the batch has been written. Persist the newly written data. + impl.persistToDisk(PersistType.Code.LITE); invokeCallbackOnResult(callback, resultBuilder.build()); } catch (Throwable t) { invokeCallbackOnError(callback, t); @@ -631,7 +635,7 @@ public class AppSearchManagerService extends SystemService { } } } - impl.persistToDisk(); + impl.persistToDisk(PersistType.Code.FULL); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(migrationFailureBundles)); } catch (Throwable t) { @@ -708,6 +712,8 @@ public class AppSearchManagerService extends SystemService { resultBuilder.setResult(uri, throwableToFailedResult(t)); } } + // Now that the batch has been written. Persist the newly written data. + impl.persistToDisk(PersistType.Code.LITE); invokeCallbackOnResult(callback, resultBuilder.build()); } catch (Throwable t) { invokeCallbackOnError(callback, t); @@ -741,6 +747,8 @@ public class AppSearchManagerService extends SystemService { databaseName, queryExpression, new SearchSpec(searchSpecBundle)); + // Now that the batch has been written. Persist the newly written data. + impl.persistToDisk(PersistType.Code.LITE); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { invokeCallbackOnError(callback, t); @@ -785,7 +793,7 @@ public class AppSearchManagerService extends SystemService { verifyUserUnlocked(callingUserId); AppSearchImpl impl = mImplInstanceManager.getAppSearchImpl(callingUserId); - impl.persistToDisk(); + impl.persistToDisk(PersistType.Code.FULL); } catch (Throwable t) { Log.e(TAG, "Unable to persist the data to disk", t); } 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 1ed26d670f36..c1b829428467 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java @@ -36,6 +36,7 @@ import android.util.ArraySet; import android.util.Log; import com.android.internal.util.Preconditions; +import com.android.server.appsearch.external.localstorage.util.PrefixUtil; import java.util.ArrayList; import java.util.Arrays; @@ -153,7 +154,7 @@ public class VisibilityStore { * AppSearchImpl. */ static final String VISIBILITY_STORE_PREFIX = - AppSearchImpl.createPrefix(PACKAGE_NAME, DATABASE_NAME); + PrefixUtil.createPrefix(PACKAGE_NAME, DATABASE_NAME); /** Namespace of documents that contain visibility settings */ private static final String NAMESPACE = ""; 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 5f8cbee3dee3..50ac054a05fc 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 @@ -16,10 +16,18 @@ package com.android.server.appsearch.external.localstorage; +import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.addPrefixToDocument; +import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPackagePrefix; +import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix; +import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getDatabaseName; +import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getPackageName; +import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getPrefix; +import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.removePrefix; +import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.removePrefixesFromDocument; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.WorkerThread; -import android.app.appsearch.AppSearchResult; import android.app.appsearch.AppSearchSchema; import android.app.appsearch.GenericDocument; import android.app.appsearch.GetByUriRequest; @@ -39,7 +47,6 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.Preconditions; import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter; import com.android.server.appsearch.external.localstorage.converter.ResultCodeToProtoConverter; import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter; @@ -66,7 +73,6 @@ import com.google.android.icing.proto.OptimizeResultProto; import com.google.android.icing.proto.PersistToDiskResultProto; import com.google.android.icing.proto.PersistType; import com.google.android.icing.proto.PropertyConfigProto; -import com.google.android.icing.proto.PropertyProto; import com.google.android.icing.proto.PutResultProto; import com.google.android.icing.proto.ReportUsageResultProto; import com.google.android.icing.proto.ResetResultProto; @@ -89,6 +95,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -132,10 +139,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; public final class AppSearchImpl implements Closeable { private static final String TAG = "AppSearchImpl"; - @VisibleForTesting static final char DATABASE_DELIMITER = '/'; - - @VisibleForTesting static final char PACKAGE_DELIMITER = '$'; - @VisibleForTesting static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000; @VisibleForTesting static final int OPTIMIZE_THRESHOLD_BYTES = 1_000_000; // 1MB @VisibleForTesting static final int CHECK_OPTIMIZE_INTERVAL = 100; @@ -148,11 +151,12 @@ public final class AppSearchImpl implements Closeable { @GuardedBy("mReadWriteLock") private final VisibilityStore mVisibilityStoreLocked; - // This map contains schemaTypes for all package-database prefixes. All values in the map are - // prefixed with the package-database prefix. - // TODO(b/172360376): Check if this can be replaced with an ArrayMap + // This map contains schema types and SchemaTypeConfigProtos for all package-database + // prefixes. It maps each package-database prefix to an inner-map. The inner-map maps each + // prefixed schema type to its respective SchemaTypeConfigProto. @GuardedBy("mReadWriteLock") - private final Map<String, Set<String>> mSchemaMapLocked = new HashMap<>(); + private final Map<String, Map<String, SchemaTypeConfigProto>> mSchemaMapLocked = + new ArrayMap<>(); // This map contains namespaces for all package-database prefixes. All values in the map are // prefixed with the package-database prefix. @@ -182,9 +186,9 @@ public final class AppSearchImpl implements Closeable { int userId, @NonNull String globalQuerierPackage) throws AppSearchException { - Preconditions.checkNotNull(icingDir); - Preconditions.checkNotNull(context); - Preconditions.checkNotNull(globalQuerierPackage); + Objects.requireNonNull(icingDir); + Objects.requireNonNull(context); + Objects.requireNonNull(globalQuerierPackage); AppSearchImpl appSearchImpl = new AppSearchImpl(icingDir, context, userId, globalQuerierPackage); appSearchImpl.initializeVisibilityStore(); @@ -229,7 +233,7 @@ public final class AppSearchImpl implements Closeable { // Populate schema map for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) { String prefixedSchemaType = schema.getSchemaType(); - addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType), prefixedSchemaType); + addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType), schema); } // Populate namespace map @@ -278,7 +282,7 @@ public final class AppSearchImpl implements Closeable { return; } - persistToDisk(); + persistToDisk(PersistType.Code.FULL); mIcingSearchEngineLocked.close(); mClosedLocked = true; } catch (AppSearchException e) { @@ -364,7 +368,14 @@ public final class AppSearchImpl implements Closeable { } // Update derived data structures. - mSchemaMapLocked.put(prefix, rewrittenSchemaResults.mRewrittenPrefixedTypes); + for (SchemaTypeConfigProto schemaTypeConfigProto : + rewrittenSchemaResults.mRewrittenPrefixedTypes.values()) { + addToMap(mSchemaMapLocked, prefix, schemaTypeConfigProto); + } + + for (String schemaType : rewrittenSchemaResults.mDeletedPrefixedTypes) { + removeFromMap(mSchemaMapLocked, prefix, schemaType); + } Set<String> prefixedSchemasNotPlatformSurfaceable = new ArraySet<>(schemasNotPlatformSurfaceable.size()); @@ -586,7 +597,7 @@ public final class AppSearchImpl implements Closeable { mReadWriteLock.readLock().lock(); try { throwIfClosedLocked(); - + String prefix = createPrefix(packageName, databaseName); List<TypePropertyMask> nonPrefixedPropertyMasks = TypePropertyPathToProtoConverter.toTypePropertyMaskList(typePropertyPaths); List<TypePropertyMask> prefixedPropertyMasks = @@ -597,7 +608,7 @@ public final class AppSearchImpl implements Closeable { String prefixedType = nonPrefixedType.equals(GetByUriRequest.PROJECTION_SCHEMA_TYPE_WILDCARD) ? nonPrefixedType - : createPrefix(packageName, databaseName) + nonPrefixedType; + : prefix + nonPrefixedType; prefixedPropertyMasks.add( typePropertyMask.toBuilder().setSchemaType(prefixedType).build()); } @@ -607,15 +618,17 @@ public final class AppSearchImpl implements Closeable { .build(); GetResultProto getResultProto = - mIcingSearchEngineLocked.get( - createPrefix(packageName, databaseName) + namespace, - uri, - getResultSpec); + mIcingSearchEngineLocked.get(prefix + namespace, uri, getResultSpec); checkSuccess(getResultProto.getStatus()); + // The schema type map cannot be null at this point. It could only be null if no + // schema had ever been set for that prefix. Given we have retrieved a document from + // the index, we know a schema had to have been set. + Map<String, SchemaTypeConfigProto> schemaTypeMap = mSchemaMapLocked.get(prefix); DocumentProto.Builder documentBuilder = getResultProto.getDocument().toBuilder(); removePrefixesFromDocument(documentBuilder); - return GenericDocumentToProtoConverter.toGenericDocument(documentBuilder.build()); + return GenericDocumentToProtoConverter.toGenericDocument( + documentBuilder.build(), prefix, schemaTypeMap); } finally { mReadWriteLock.readLock().unlock(); } @@ -726,7 +739,7 @@ public final class AppSearchImpl implements Closeable { } } else { // Client didn't specify certain schemas to search over, check all schemas - Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix); + Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix).keySet(); if (prefixedSchemas != null) { for (String prefixedSchema : prefixedSchemas) { if (packageName.equals(callerPackageName) @@ -816,7 +829,7 @@ public final class AppSearchImpl implements Closeable { searchSpecBuilder.build(), scoringSpec, resultSpecBuilder.build()); checkSuccess(searchResultProto.getStatus()); - return rewriteSearchResultProto(searchResultProto); + return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked); } /** @@ -838,7 +851,7 @@ public final class AppSearchImpl implements Closeable { SearchResultProto searchResultProto = mIcingSearchEngineLocked.getNextPage(nextPageToken); checkSuccess(searchResultProto.getStatus()); - return rewriteSearchResultProto(searchResultProto); + return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked); } finally { mReadWriteLock.readLock().unlock(); } @@ -1104,22 +1117,31 @@ public final class AppSearchImpl implements Closeable { /** * Persists all update/delete requests to the disk. * - * <p>If the app crashes after a call to PersistToDisk(), Icing would be able to fully recover - * all data written up to this point without a costly recovery process. + * <p>If the app crashes after a call to PersistToDisk with {@link PersistType.Code#FULL}, Icing + * would be able to fully recover all data written up to this point without a costly recovery + * process. + * + * <p>If the app crashes after a call to PersistToDisk with {@link PersistType.Code#LITE}, Icing + * would trigger a costly recovery process in next initialization. After that, Icing would still + * be able to recover all written data - excepting Usage data. Usage data is only guaranteed to + * be safe after a call to PersistToDisk with {@link PersistType.Code#FULL} * - * <p>If the app crashes before a call to PersistToDisk(), Icing would trigger a costly recovery - * process in next initialization. After that, Icing would still be able to recover all written - * data. + * <p>If the app crashes after an update/delete request has been made, but before any call to + * PersistToDisk, then all data in Icing will be lost. * + * @param persistType the amount of data to persist. {@link PersistType.Code#LITE} will only + * persist the minimal amount of data to ensure all data can be recovered. {@link + * PersistType.Code#FULL} will persist all data necessary to prevent data loss without + * needing data recovery. * @throws AppSearchException on any error that AppSearch persist data to disk. */ - public void persistToDisk() throws AppSearchException { + public void persistToDisk(@NonNull PersistType.Code persistType) throws AppSearchException { mReadWriteLock.writeLock().lock(); try { throwIfClosedLocked(); PersistToDiskResultProto persistToDiskResultProto = - mIcingSearchEngineLocked.persistToDisk(PersistType.Code.FULL); + mIcingSearchEngineLocked.persistToDisk(persistType); checkSuccess(persistToDiskResultProto.getStatus()); } finally { mReadWriteLock.writeLock().unlock(); @@ -1189,8 +1211,8 @@ public final class AppSearchImpl implements Closeable { // Any prefixed types that used to exist in the schema, but are deleted in the new one. final Set<String> mDeletedPrefixedTypes = new ArraySet<>(); - // Prefixed types that were part of the new schema. - final Set<String> mRewrittenPrefixedTypes = new ArraySet<>(); + // Map of prefixed schema types to SchemaTypeConfigProtos that were part of the new schema. + final Map<String, SchemaTypeConfigProto> mRewrittenPrefixedTypes = new ArrayMap<>(); } /** @@ -1238,7 +1260,7 @@ public final class AppSearchImpl implements Closeable { // newTypesToProto is modified below, so we need a copy first RewrittenSchemaResults rewrittenSchemaResults = new RewrittenSchemaResults(); - rewrittenSchemaResults.mRewrittenPrefixedTypes.addAll(newTypesToProto.keySet()); + rewrittenSchemaResults.mRewrittenPrefixedTypes.putAll(newTypesToProto); // Combine the existing schema (which may have types from other prefixes) with this // prefix's new schema. Modifies the existingSchemaBuilder. @@ -1264,99 +1286,6 @@ public final class AppSearchImpl implements Closeable { } /** - * Prepends {@code prefix} to all types and namespaces mentioned anywhere in {@code - * documentBuilder}. - * - * @param documentBuilder The document to mutate - * @param prefix The prefix to add - */ - @VisibleForTesting - static void addPrefixToDocument( - @NonNull DocumentProto.Builder documentBuilder, @NonNull String prefix) { - // Rewrite the type name to include/remove the prefix. - String newSchema = prefix + documentBuilder.getSchema(); - documentBuilder.setSchema(newSchema); - - // Rewrite the namespace to include/remove the prefix. - documentBuilder.setNamespace(prefix + documentBuilder.getNamespace()); - - // Recurse into derived documents - for (int propertyIdx = 0; - propertyIdx < documentBuilder.getPropertiesCount(); - propertyIdx++) { - int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount(); - if (documentCount > 0) { - PropertyProto.Builder propertyBuilder = - documentBuilder.getProperties(propertyIdx).toBuilder(); - for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) { - DocumentProto.Builder derivedDocumentBuilder = - propertyBuilder.getDocumentValues(documentIdx).toBuilder(); - addPrefixToDocument(derivedDocumentBuilder, prefix); - propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder); - } - documentBuilder.setProperties(propertyIdx, propertyBuilder); - } - } - } - - /** - * 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 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())); - - // Recurse into derived documents - for (int propertyIdx = 0; - propertyIdx < documentBuilder.getPropertiesCount(); - propertyIdx++) { - int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount(); - if (documentCount > 0) { - PropertyProto.Builder propertyBuilder = - documentBuilder.getProperties(propertyIdx).toBuilder(); - for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) { - DocumentProto.Builder derivedDocumentBuilder = - propertyBuilder.getDocumentValues(documentIdx).toBuilder(); - 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; - } - - /** * Rewrites the search spec filters with {@code prefixes}. * * <p>This method should be only called in query methods and get the READ lock to keep thread @@ -1443,9 +1372,9 @@ public final class AppSearchImpl implements Closeable { if (allowedPrefixedSchemas.isEmpty()) { // If the client didn't specify any schema filters, search over all of their schemas - Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix); - if (prefixedSchemas != null) { - allowedPrefixedSchemas.addAll(prefixedSchemas); + Map<String, SchemaTypeConfigProto> prefixedSchemaMap = mSchemaMapLocked.get(prefix); + if (prefixedSchemaMap != null) { + allowedPrefixedSchemas.addAll(prefixedSchemaMap.keySet()); } } return allowedPrefixedSchemas; @@ -1656,86 +1585,6 @@ public final class AppSearchImpl implements Closeable { return mSchemaMapLocked.keySet(); } - @NonNull - static String createPrefix(@NonNull String packageName, @NonNull String databaseName) { - return createPackagePrefix(packageName) + databaseName + DATABASE_DELIMITER; - } - - @NonNull - private static String createPackagePrefix(@NonNull String packageName) { - return packageName + PACKAGE_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 delimiter: " + prefix); - return ""; - } - return prefix.substring(0, delimiterIndex); - } - - /** - * Returns the database name that's contained within the {@code prefix}. - * - * @param prefix Prefix string that contains the database name inside of it. The database name - * must be between the {@link #PACKAGE_DELIMITER} and {@link #DATABASE_DELIMITER} - * @return Valid database name. - */ - @NonNull - private static String getDatabaseName(@NonNull String prefix) { - int packageDelimiterIndex = prefix.indexOf(PACKAGE_DELIMITER); - int databaseDelimiterIndex = prefix.indexOf(DATABASE_DELIMITER); - if (packageDelimiterIndex == -1) { - // This should never happen if we construct our prefixes properly - Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix); - return ""; - } - if (databaseDelimiterIndex == -1) { - // This should never happen if we construct our prefixes properly - Log.wtf(TAG, "Malformed prefix doesn't contain database delimiter: " + prefix); - return ""; - } - return prefix.substring(packageDelimiterIndex + 1, databaseDelimiterIndex); - } - - @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 - // database cutoff. - int delimiterIndex; - if ((delimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER)) != -1) { - // Add 1 to include the char size of the DATABASE_DELIMITER - return prefixedString.substring(delimiterIndex + 1); - } - throw new AppSearchException( - AppSearchResult.RESULT_UNKNOWN_ERROR, - "The prefixed value doesn't contains a valid database name."); - } - - @NonNull - private static String getPrefix(@NonNull String prefixedString) throws AppSearchException { - int databaseDelimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER); - if (databaseDelimiterIndex == -1) { - throw new AppSearchException( - AppSearchResult.RESULT_UNKNOWN_ERROR, - "The databaseName prefixed value doesn't contain a valid database name."); - } - - // Add 1 to include the char size of the DATABASE_DELIMITER - return prefixedString.substring(0, databaseDelimiterIndex + 1); - } - private static void addToMap( Map<String, Set<String>> map, String prefix, String prefixedValue) { Set<String> values = map.get(prefix); @@ -1746,6 +1595,26 @@ public final class AppSearchImpl implements Closeable { values.add(prefixedValue); } + private static void addToMap( + Map<String, Map<String, SchemaTypeConfigProto>> map, + String prefix, + SchemaTypeConfigProto schemaTypeConfigProto) { + Map<String, SchemaTypeConfigProto> schemaTypeMap = map.get(prefix); + if (schemaTypeMap == null) { + schemaTypeMap = new ArrayMap<>(); + map.put(prefix, schemaTypeMap); + } + schemaTypeMap.put(schemaTypeConfigProto.getSchemaType(), schemaTypeConfigProto); + } + + private static void removeFromMap( + Map<String, Map<String, SchemaTypeConfigProto>> map, String prefix, String schemaType) { + Map<String, SchemaTypeConfigProto> schemaTypeMap = map.get(prefix); + if (schemaTypeMap != null) { + schemaTypeMap.remove(schemaType); + } + } + /** * Checks the given status code and throws an {@link AppSearchException} if code is an error. * @@ -1853,7 +1722,9 @@ public final class AppSearchImpl implements Closeable { /** Remove the rewritten schema types from any result documents. */ @NonNull @VisibleForTesting - static SearchResultPage rewriteSearchResultProto(@NonNull SearchResultProto searchResultProto) + static SearchResultPage rewriteSearchResultProto( + @NonNull SearchResultProto searchResultProto, + @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) throws AppSearchException { // Parallel array of package names for each document search result. List<String> packageNames = new ArrayList<>(searchResultProto.getResultsCount()); @@ -1873,7 +1744,7 @@ public final class AppSearchImpl implements Closeable { resultsBuilder.setResults(i, resultBuilder); } return SearchResultToProtoConverter.toSearchResultPage( - resultsBuilder, packageNames, databaseNames); + resultsBuilder, packageNames, databaseNames, schemaMap); } @GuardedBy("mReadWriteLock") diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java index 5680670629ae..cdd795207e0b 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java @@ -18,11 +18,12 @@ package com.android.server.appsearch.external.localstorage; import android.annotation.NonNull; -import com.android.internal.util.Preconditions; import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; import com.google.android.icing.proto.PutDocumentStatsProto; +import java.util.Objects; + /** * Class contains helper functions for logging. * @@ -42,8 +43,8 @@ public final class AppSearchLoggerHelper { static void copyNativeStats( @NonNull PutDocumentStatsProto fromNativeStats, @NonNull PutDocumentStats.Builder toStatsBuilder) { - Preconditions.checkNotNull(fromNativeStats); - Preconditions.checkNotNull(toStatsBuilder); + Objects.requireNonNull(fromNativeStats); + Objects.requireNonNull(toStatsBuilder); toStatsBuilder .setNativeLatencyMillis(fromNativeStats.getLatencyMs()) .setNativeDocumentStoreLatencyMillis(fromNativeStats.getDocumentStoreLatencyMs()) 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 d6b9da827515..5ff56abd870a 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 @@ -17,16 +17,18 @@ package com.android.server.appsearch.external.localstorage.converter; import android.annotation.NonNull; +import android.app.appsearch.AppSearchSchema; import android.app.appsearch.GenericDocument; -import com.android.internal.util.Preconditions; - import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.PropertyProto; +import com.google.android.icing.proto.SchemaTypeConfigProto; import com.google.protobuf.ByteString; import java.util.ArrayList; import java.util.Collections; +import java.util.Map; +import java.util.Objects; /** * Translates a {@link GenericDocument} into a {@link DocumentProto}. @@ -34,13 +36,20 @@ import java.util.Collections; * @hide */ public final class GenericDocumentToProtoConverter { + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + private static final long[] EMPTY_LONG_ARRAY = new long[0]; + private static final double[] EMPTY_DOUBLE_ARRAY = new double[0]; + private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0]; + private static final byte[][] EMPTY_BYTES_ARRAY = new byte[0][0]; + private static final GenericDocument[] EMPTY_DOCUMENT_ARRAY = new GenericDocument[0]; + private GenericDocumentToProtoConverter() {} /** Converts a {@link GenericDocument} into a {@link DocumentProto}. */ @NonNull @SuppressWarnings("unchecked") public static DocumentProto toDocumentProto(@NonNull GenericDocument document) { - Preconditions.checkNotNull(document); + Objects.requireNonNull(document); DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder(); mProtoBuilder .setUri(document.getUri()) @@ -97,16 +106,34 @@ public final class GenericDocumentToProtoConverter { return mProtoBuilder.build(); } - /** Converts a {@link DocumentProto} into a {@link GenericDocument}. */ + /** + * Converts a {@link DocumentProto} into a {@link GenericDocument}. + * + * <p>In the case that the {@link DocumentProto} object proto has no values set, the converter + * searches for the matching property name in the {@link SchemaTypeConfigProto} object for the + * document, and infers the correct default value to set for the empty property based on the + * data type of the property defined by the schema type. + * + * @param proto the document to convert to a {@link GenericDocument} instance. The document + * proto should have its package + database prefix stripped from its fields. + * @param prefix the package + database prefix used searching the {@code schemaTypeMap}. + * @param schemaTypeMap map of prefixed schema type to {@link SchemaTypeConfigProto}, used for + * looking up the default empty value to set for a document property that has all empty + * values. + */ @NonNull - public static GenericDocument toGenericDocument(@NonNull DocumentProto proto) { - Preconditions.checkNotNull(proto); + public static GenericDocument toGenericDocument( + @NonNull DocumentProto proto, + @NonNull String prefix, + @NonNull Map<String, SchemaTypeConfigProto> schemaTypeMap) { + Objects.requireNonNull(proto); GenericDocument.Builder<?> documentBuilder = new GenericDocument.Builder<>( proto.getNamespace(), proto.getUri(), proto.getSchema()) .setScore(proto.getScore()) .setTtlMillis(proto.getTtlMs()) .setCreationTimestampMillis(proto.getCreationTimestampMs()); + String prefixedSchemaType = prefix + proto.getSchema(); for (int i = 0; i < proto.getPropertiesCount(); i++) { PropertyProto property = proto.getProperties(i); @@ -144,13 +171,51 @@ public final class GenericDocumentToProtoConverter { } else if (property.getDocumentValuesCount() > 0) { GenericDocument[] values = new GenericDocument[property.getDocumentValuesCount()]; for (int j = 0; j < values.length; j++) { - values[j] = toGenericDocument(property.getDocumentValues(j)); + values[j] = + toGenericDocument(property.getDocumentValues(j), prefix, schemaTypeMap); } documentBuilder.setPropertyDocument(name, values); } else { - throw new IllegalStateException("Unknown type of value: " + name); + // TODO(b/184966497): Optimize by caching PropertyConfigProto + setEmptyProperty(name, documentBuilder, schemaTypeMap.get(prefixedSchemaType)); } } return documentBuilder.build(); } + + private static void setEmptyProperty( + @NonNull String propertyName, + @NonNull GenericDocument.Builder<?> documentBuilder, + @NonNull SchemaTypeConfigProto schema) { + @AppSearchSchema.PropertyConfig.DataType int dataType = 0; + for (int i = 0; i < schema.getPropertiesCount(); ++i) { + if (propertyName.equals(schema.getProperties(i).getPropertyName())) { + dataType = schema.getProperties(i).getDataType().getNumber(); + break; + } + } + + switch (dataType) { + case AppSearchSchema.PropertyConfig.DATA_TYPE_STRING: + documentBuilder.setPropertyString(propertyName, EMPTY_STRING_ARRAY); + break; + case AppSearchSchema.PropertyConfig.DATA_TYPE_INT64: + documentBuilder.setPropertyLong(propertyName, EMPTY_LONG_ARRAY); + break; + case AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE: + documentBuilder.setPropertyDouble(propertyName, EMPTY_DOUBLE_ARRAY); + break; + case AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN: + documentBuilder.setPropertyBoolean(propertyName, EMPTY_BOOLEAN_ARRAY); + break; + case AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES: + documentBuilder.setPropertyBytes(propertyName, EMPTY_BYTES_ARRAY); + break; + case AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT: + documentBuilder.setPropertyDocument(propertyName, EMPTY_DOCUMENT_ARRAY); + break; + default: + throw new IllegalStateException("Unknown type of value: " + propertyName); + } + } } 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 800b073fd44f..e3fa7e08aac4 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 @@ -20,8 +20,6 @@ import android.annotation.NonNull; import android.app.appsearch.AppSearchSchema; import android.util.Log; -import com.android.internal.util.Preconditions; - import com.google.android.icing.proto.DocumentIndexingConfig; import com.google.android.icing.proto.PropertyConfigProto; import com.google.android.icing.proto.SchemaTypeConfigProto; @@ -30,6 +28,7 @@ import com.google.android.icing.proto.StringIndexingConfig; import com.google.android.icing.proto.TermMatchType; import java.util.List; +import java.util.Objects; /** * Translates an {@link AppSearchSchema} into a {@link SchemaTypeConfigProto}. @@ -48,7 +47,7 @@ public final class SchemaToProtoConverter { @NonNull public static SchemaTypeConfigProto toSchemaTypeConfigProto( @NonNull AppSearchSchema schema, int version) { - Preconditions.checkNotNull(schema); + Objects.requireNonNull(schema); SchemaTypeConfigProto.Builder protoBuilder = SchemaTypeConfigProto.newBuilder() .setSchemaType(schema.getSchemaType()) @@ -64,7 +63,7 @@ public final class SchemaToProtoConverter { @NonNull private static PropertyConfigProto toPropertyConfigProto( @NonNull AppSearchSchema.PropertyConfig property) { - Preconditions.checkNotNull(property); + Objects.requireNonNull(property); PropertyConfigProto.Builder builder = PropertyConfigProto.newBuilder().setPropertyName(property.getName()); @@ -116,7 +115,7 @@ public final class SchemaToProtoConverter { */ @NonNull public static AppSearchSchema toAppSearchSchema(@NonNull SchemaTypeConfigProtoOrBuilder proto) { - Preconditions.checkNotNull(proto); + Objects.requireNonNull(proto); AppSearchSchema.Builder builder = new AppSearchSchema.Builder(proto.getSchemaType()); List<PropertyConfigProto> properties = proto.getPropertiesList(); for (int i = 0; i < properties.size(); i++) { @@ -129,7 +128,7 @@ public final class SchemaToProtoConverter { @NonNull private static AppSearchSchema.PropertyConfig toPropertyConfig( @NonNull PropertyConfigProto proto) { - Preconditions.checkNotNull(proto); + Objects.requireNonNull(proto); switch (proto.getDataType()) { case STRING: return toStringPropertyConfig(proto); 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 bf7e5334d090..57c1590d2bef 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 @@ -16,6 +16,8 @@ package com.android.server.appsearch.external.localstorage.converter; +import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix; + import android.annotation.NonNull; import android.app.appsearch.GenericDocument; import android.app.appsearch.SearchResult; @@ -24,6 +26,7 @@ import android.os.Bundle; import com.android.internal.util.Preconditions; +import com.google.android.icing.proto.SchemaTypeConfigProto; import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.SearchResultProtoOrBuilder; import com.google.android.icing.proto.SnippetMatchProto; @@ -31,6 +34,7 @@ import com.google.android.icing.proto.SnippetProto; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * Translates a {@link SearchResultProto} into {@link SearchResult}s. @@ -49,13 +53,17 @@ public class SearchResultToProtoConverter { * @param databaseNames A parallel array of database names. The database name at index 'i' of * this list shold be the database that indexed the document at index 'i' of * proto.getResults(i). + * @param schemaMap A map of prefixes to an inner-map of prefixed schema type to + * SchemaTypeConfigProtos, used for setting a default value for results with DocumentProtos + * that have empty values. * @return {@link SearchResultPage} of results. */ @NonNull public static SearchResultPage toSearchResultPage( @NonNull SearchResultProtoOrBuilder proto, @NonNull List<String> packageNames, - @NonNull List<String> databaseNames) { + @NonNull List<String> databaseNames, + @NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) { Preconditions.checkArgument( proto.getResultsCount() == packageNames.size(), "Size of results does not match the number of package names."); @@ -63,8 +71,14 @@ public class SearchResultToProtoConverter { bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken()); ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount()); for (int i = 0; i < proto.getResultsCount(); i++) { + String prefix = createPrefix(packageNames.get(i), databaseNames.get(i)); + Map<String, SchemaTypeConfigProto> schemaTypeMap = schemaMap.get(prefix); SearchResult result = - toSearchResult(proto.getResults(i), packageNames.get(i), databaseNames.get(i)); + toSearchResult( + proto.getResults(i), + packageNames.get(i), + databaseNames.get(i), + schemaTypeMap); resultBundles.add(result.getBundle()); } bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles); @@ -77,15 +91,21 @@ public class SearchResultToProtoConverter { * @param proto The proto to be converted. * @param packageName The package name associated with the document in {@code proto}. * @param databaseName The database name associated with the document in {@code proto}. + * @param schemaTypeToProtoMap A map of prefixed schema types to their corresponding + * SchemaTypeConfigProto, used for setting a default value for results with DocumentProtos + * that have empty values. * @return A {@link SearchResult} bundle. */ @NonNull private static SearchResult toSearchResult( @NonNull SearchResultProto.ResultProtoOrBuilder proto, @NonNull String packageName, - @NonNull String databaseName) { + @NonNull String databaseName, + @NonNull Map<String, SchemaTypeConfigProto> schemaTypeToProtoMap) { + String prefix = createPrefix(packageName, databaseName); GenericDocument document = - GenericDocumentToProtoConverter.toGenericDocument(proto.getDocument()); + GenericDocumentToProtoConverter.toGenericDocument( + proto.getDocument(), prefix, schemaTypeToProtoMap); SearchResult.Builder builder = new SearchResult.Builder(packageName, databaseName) .setGenericDocument(document) diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java index d9e8adb52e5e..8f9e9bdc8cbc 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java @@ -19,13 +19,13 @@ package com.android.server.appsearch.external.localstorage.converter; import android.annotation.NonNull; import android.app.appsearch.SearchSpec; -import com.android.internal.util.Preconditions; - import com.google.android.icing.proto.ResultSpecProto; import com.google.android.icing.proto.ScoringSpecProto; import com.google.android.icing.proto.SearchSpecProto; import com.google.android.icing.proto.TermMatchType; +import java.util.Objects; + /** * Translates a {@link SearchSpec} into icing search protos. * @@ -37,7 +37,7 @@ public final class SearchSpecToProtoConverter { /** Extracts {@link SearchSpecProto} information from a {@link SearchSpec}. */ @NonNull public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) { - Preconditions.checkNotNull(spec); + Objects.requireNonNull(spec); SearchSpecProto.Builder protoBuilder = SearchSpecProto.newBuilder() .addAllSchemaTypeFilters(spec.getFilterSchemas()) @@ -56,7 +56,7 @@ public final class SearchSpecToProtoConverter { /** Extracts {@link ResultSpecProto} information from a {@link SearchSpec}. */ @NonNull public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) { - Preconditions.checkNotNull(spec); + Objects.requireNonNull(spec); return ResultSpecProto.newBuilder() .setNumPerPage(spec.getResultCountPerPage()) .setSnippetSpec( @@ -73,7 +73,7 @@ public final class SearchSpecToProtoConverter { /** Extracts {@link ScoringSpecProto} information from a {@link SearchSpec}. */ @NonNull public static ScoringSpecProto toScoringSpecProto(@NonNull SearchSpec spec) { - Preconditions.checkNotNull(spec); + Objects.requireNonNull(spec); ScoringSpecProto.Builder protoBuilder = ScoringSpecProto.newBuilder(); @SearchSpec.Order int orderCode = spec.getOrder(); diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java index a0f39ecd68fb..ed73593fd1a2 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java @@ -19,10 +19,10 @@ package com.android.server.appsearch.external.localstorage.converter; import android.annotation.NonNull; import android.app.appsearch.SetSchemaResponse; -import com.android.internal.util.Preconditions; - import com.google.android.icing.proto.SetSchemaResultProto; +import java.util.Objects; + /** * Translates a {@link SetSchemaResultProto} into {@link SetSchemaResponse}. * @@ -42,8 +42,8 @@ public class SetSchemaResponseToProtoConverter { @NonNull public static SetSchemaResponse toSetSchemaResponse( @NonNull SetSchemaResultProto proto, @NonNull String prefix) { - Preconditions.checkNotNull(proto); - Preconditions.checkNotNull(prefix); + Objects.requireNonNull(proto); + Objects.requireNonNull(prefix); SetSchemaResponse.Builder builder = new SetSchemaResponse.Builder(); for (int i = 0; i < proto.getDeletedSchemaTypesCount(); i++) { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java index 6f6dad207713..acf04ef08a2c 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java @@ -18,13 +18,12 @@ package com.android.server.appsearch.external.localstorage.converter; import android.annotation.NonNull; -import com.android.internal.util.Preconditions; - import com.google.android.icing.proto.TypePropertyMask; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; /** * Translates a <code>Map<String, List<String>></code> into <code>List<TypePropertyMask></code>. @@ -38,7 +37,7 @@ public final class TypePropertyPathToProtoConverter { @NonNull public static List<TypePropertyMask> toTypePropertyMaskList( @NonNull Map<String, List<String>> typePropertyPaths) { - Preconditions.checkNotNull(typePropertyPaths); + Objects.requireNonNull(typePropertyPaths); List<TypePropertyMask> typePropertyMasks = new ArrayList<>(typePropertyPaths.size()); for (Map.Entry<String, List<String>> e : typePropertyPaths.entrySet()) { typePropertyMasks.add( diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java index a724f95ad8fa..cf640c1a8d21 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java @@ -19,10 +19,9 @@ package com.android.server.appsearch.external.localstorage.stats; import android.annotation.IntDef; import android.annotation.NonNull; -import com.android.internal.util.Preconditions; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * A class for setting basic information to log for all function calls. @@ -75,8 +74,8 @@ public class CallStats { private final int mNumOperationsFailed; CallStats(@NonNull Builder builder) { - Preconditions.checkNotNull(builder); - mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStatsBuilder).build(); + Objects.requireNonNull(builder); + mGeneralStats = Objects.requireNonNull(builder.mGeneralStatsBuilder).build(); mCallType = builder.mCallType; mEstimatedBinderLatencyMillis = builder.mEstimatedBinderLatencyMillis; mNumOperationsSucceeded = builder.mNumOperationsSucceeded; @@ -140,8 +139,8 @@ public class CallStats { /** Builder takes {@link GeneralStats.Builder}. */ public Builder(@NonNull String packageName, @NonNull String database) { - Preconditions.checkNotNull(packageName); - Preconditions.checkNotNull(database); + Objects.requireNonNull(packageName); + Objects.requireNonNull(database); mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database); } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java index 8ce8eda3d7a7..53c1ee3f675f 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java @@ -19,7 +19,7 @@ package com.android.server.appsearch.external.localstorage.stats; import android.annotation.NonNull; import android.app.appsearch.AppSearchResult; -import com.android.internal.util.Preconditions; +import java.util.Objects; /** * A class for holding general logging information. @@ -48,9 +48,9 @@ public final class GeneralStats { private final int mTotalLatencyMillis; GeneralStats(@NonNull Builder builder) { - Preconditions.checkNotNull(builder); - mPackageName = Preconditions.checkNotNull(builder.mPackageName); - mDatabase = Preconditions.checkNotNull(builder.mDatabase); + Objects.requireNonNull(builder); + mPackageName = Objects.requireNonNull(builder.mPackageName); + mDatabase = Objects.requireNonNull(builder.mDatabase); mStatusCode = builder.mStatusCode; mTotalLatencyMillis = builder.mTotalLatencyMillis; } @@ -92,8 +92,8 @@ public final class GeneralStats { * @param database name of the database logging stats */ public Builder(@NonNull String packageName, @NonNull String database) { - mPackageName = Preconditions.checkNotNull(packageName); - mDatabase = Preconditions.checkNotNull(database); + mPackageName = Objects.requireNonNull(packageName); + mDatabase = Objects.requireNonNull(database); } /** Sets status code returned from {@link AppSearchResult#getResultCode()} */ diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java index c1f6fb118797..d031172d29c1 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java @@ -18,7 +18,7 @@ package com.android.server.appsearch.external.localstorage.stats; import android.annotation.NonNull; -import com.android.internal.util.Preconditions; +import java.util.Objects; /** * A class for holding detailed stats to log for each individual document put by a {@link @@ -60,8 +60,8 @@ public final class PutDocumentStats { private final boolean mNativeExceededMaxNumTokens; PutDocumentStats(@NonNull Builder builder) { - Preconditions.checkNotNull(builder); - mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStatsBuilder).build(); + Objects.requireNonNull(builder); + mGeneralStats = Objects.requireNonNull(builder.mGeneralStatsBuilder).build(); mGenerateDocumentProtoLatencyMillis = builder.mGenerateDocumentProtoLatencyMillis; mRewriteDocumentTypesLatencyMillis = builder.mRewriteDocumentTypesLatencyMillis; mNativeLatencyMillis = builder.mNativeLatencyMillis; @@ -142,8 +142,8 @@ public final class PutDocumentStats { /** Builder takes {@link GeneralStats.Builder}. */ public Builder(@NonNull String packageName, @NonNull String database) { - Preconditions.checkNotNull(packageName); - Preconditions.checkNotNull(database); + Objects.requireNonNull(packageName); + Objects.requireNonNull(database); mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database); } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java new file mode 100644 index 000000000000..9ae9f1852849 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java @@ -0,0 +1,229 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appsearch.external.localstorage.util; + +import android.annotation.NonNull; +import android.app.appsearch.AppSearchResult; +import android.app.appsearch.exceptions.AppSearchException; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +import com.google.android.icing.proto.DocumentProto; +import com.google.android.icing.proto.PropertyProto; + +/** + * Provides utility functions for working with package + database prefixes. + * + * @hide + */ +public class PrefixUtil { + private static final String TAG = "AppSearchPrefixUtil"; + + @VisibleForTesting public static final char DATABASE_DELIMITER = '/'; + + @VisibleForTesting public static final char PACKAGE_DELIMITER = '$'; + + private PrefixUtil() {} + + /** Creates prefix string for given package name and database name. */ + @NonNull + public static String createPrefix(@NonNull String packageName, @NonNull String databaseName) { + return packageName + PACKAGE_DELIMITER + databaseName + DATABASE_DELIMITER; + } + /** Creates prefix string for given package name. */ + @NonNull + public static String createPackagePrefix(@NonNull String packageName) { + return packageName + PACKAGE_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 + public 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 delimiter: " + prefix); + return ""; + } + return prefix.substring(0, delimiterIndex); + } + + /** + * Returns the database name that's contained within the {@code prefix}. + * + * @param prefix Prefix string that contains the database name inside of it. The database name + * must be between the {@link #PACKAGE_DELIMITER} and {@link #DATABASE_DELIMITER} + * @return Valid database name. + */ + @NonNull + public static String getDatabaseName(@NonNull String prefix) { + // TODO (b/184050178) Start database delimiter index search from after package delimiter + int packageDelimiterIndex = prefix.indexOf(PACKAGE_DELIMITER); + int databaseDelimiterIndex = prefix.indexOf(DATABASE_DELIMITER); + if (packageDelimiterIndex == -1) { + // This should never happen if we construct our prefixes properly + Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix); + return ""; + } + if (databaseDelimiterIndex == -1) { + // This should never happen if we construct our prefixes properly + Log.wtf(TAG, "Malformed prefix doesn't contain database delimiter: " + prefix); + return ""; + } + return prefix.substring(packageDelimiterIndex + 1, databaseDelimiterIndex); + } + + /** + * Creates a string with the package and database prefix removed from the input string. + * + * @param prefixedString a string containing a package and database prefix. + * @return a string with the package and database prefix removed. + * @throws AppSearchException if the prefixed value does not contain a valid database name. + */ + @NonNull + public 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 + // database cutoff. + int delimiterIndex; + if ((delimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER)) != -1) { + // Add 1 to include the char size of the DATABASE_DELIMITER + return prefixedString.substring(delimiterIndex + 1); + } + throw new AppSearchException( + AppSearchResult.RESULT_UNKNOWN_ERROR, + "The prefixed value doesn't contains a valid database name."); + } + + /** + * Creates a package and database prefix string from the input string. + * + * @param prefixedString a string containing a package and database prefix. + * @return a string with the package and database prefix + * @throws AppSearchException if the prefixed value does not contain a valid database name. + */ + @NonNull + public static String getPrefix(@NonNull String prefixedString) throws AppSearchException { + int databaseDelimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER); + if (databaseDelimiterIndex == -1) { + throw new AppSearchException( + AppSearchResult.RESULT_UNKNOWN_ERROR, + "The databaseName prefixed value doesn't contain a valid database name."); + } + + // Add 1 to include the char size of the DATABASE_DELIMITER + return prefixedString.substring(0, databaseDelimiterIndex + 1); + } + + /** + * Prepends {@code prefix} to all types and namespaces mentioned anywhere in {@code + * documentBuilder}. + * + * @param documentBuilder The document to mutate + * @param prefix The prefix to add + */ + public static void addPrefixToDocument( + @NonNull DocumentProto.Builder documentBuilder, @NonNull String prefix) { + // Rewrite the type name to include/remove the prefix. + String newSchema = prefix + documentBuilder.getSchema(); + documentBuilder.setSchema(newSchema); + + // Rewrite the namespace to include/remove the prefix. + documentBuilder.setNamespace(prefix + documentBuilder.getNamespace()); + + // Recurse into derived documents + for (int propertyIdx = 0; + propertyIdx < documentBuilder.getPropertiesCount(); + propertyIdx++) { + int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount(); + if (documentCount > 0) { + PropertyProto.Builder propertyBuilder = + documentBuilder.getProperties(propertyIdx).toBuilder(); + for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) { + DocumentProto.Builder derivedDocumentBuilder = + propertyBuilder.getDocumentValues(documentIdx).toBuilder(); + addPrefixToDocument(derivedDocumentBuilder, prefix); + propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder); + } + documentBuilder.setProperties(propertyIdx, propertyBuilder); + } + } + } + + /** + * 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 + public 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())); + + // Recurse into derived documents + for (int propertyIdx = 0; + propertyIdx < documentBuilder.getPropertiesCount(); + propertyIdx++) { + int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount(); + if (documentCount > 0) { + PropertyProto.Builder propertyBuilder = + documentBuilder.getProperties(propertyIdx).toBuilder(); + for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) { + DocumentProto.Builder derivedDocumentBuilder = + propertyBuilder.getDocumentValues(documentIdx).toBuilder(); + 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; + } +} diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index e46c147e36ff..f99664b4c685 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -I925ec12f4901c7759976c344ba3428210aada8ad +If9d1d770d2327d7d0db7d82acfc54787b5de64bc 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 206904372236..494945db5655 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 @@ -187,9 +187,8 @@ public interface AppSearchSessionShim extends Closeable { * <p>Removed documents will no longer be surfaced by {@link #search} or {@link #getByUri} * 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. * @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java new file mode 100644 index 000000000000..de0670b08ffd --- /dev/null +++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java @@ -0,0 +1,41 @@ +/* + * 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; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.expectThrows; + +import org.junit.Test; + +public class AppSearchResultTest { + @Test + public void testMapNullPointerException() { + NullPointerException e = + expectThrows( + NullPointerException.class, + () -> { + Object o = null; + o.toString(); + }); + AppSearchResult<?> result = AppSearchResult.throwableToFailedResult(e); + assertThat(result.getResultCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR); + // Makes sure the exception name is included in the string. Some exceptions have terse or + // missing strings so it's confusing to read the output without the exception name. + assertThat(result.getErrorMessage()).startsWith("NullPointerException"); + } +} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java index 4240581bc504..b552fd5e8718 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java @@ -32,6 +32,8 @@ import android.content.pm.PackageManager; import androidx.test.core.app.ApplicationProvider; +import com.android.server.appsearch.external.localstorage.util.PrefixUtil; + import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -203,7 +205,7 @@ public class AppSearchImplPlatformTest { mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo); // Set schema1 - String prefix = AppSearchImpl.createPrefix("package", "database"); + String prefix = PrefixUtil.createPrefix("package", "database"); mAppSearchImpl.setSchema( "package", "database", @@ -280,7 +282,7 @@ public class AppSearchImplPlatformTest { mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo); mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo); - String prefix = AppSearchImpl.createPrefix("package", "database"); + String prefix = PrefixUtil.createPrefix("package", "database"); mAppSearchImpl.setSchema( "package", "database", @@ -353,7 +355,7 @@ public class AppSearchImplPlatformTest { @Test public void testSetSchema_defaultPlatformVisible() throws Exception { - String prefix = AppSearchImpl.createPrefix("package", "database"); + String prefix = PrefixUtil.createPrefix("package", "database"); mAppSearchImpl.setSchema( "package", "database", @@ -372,7 +374,7 @@ public class AppSearchImplPlatformTest { @Test public void testSetSchema_platformHidden() throws Exception { - String prefix = AppSearchImpl.createPrefix("package", "database"); + String prefix = PrefixUtil.createPrefix("package", "database"); mAppSearchImpl.setSchema( "package", "database", @@ -391,7 +393,7 @@ public class AppSearchImplPlatformTest { @Test public void testSetSchema_defaultNotPackageAccessible() throws Exception { - String prefix = AppSearchImpl.createPrefix("package", "database"); + String prefix = PrefixUtil.createPrefix("package", "database"); mAppSearchImpl.setSchema( "package", "database", @@ -419,7 +421,7 @@ public class AppSearchImplPlatformTest { mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo); mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo); - String prefix = AppSearchImpl.createPrefix("package", "database"); + String prefix = PrefixUtil.createPrefix("package", "database"); mAppSearchImpl.setSchema( "package", "database", diff --git a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java index 8d35ebe8e2e1..11ae76b94495 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/VisibilityStoreTest.java @@ -28,6 +28,8 @@ import android.content.pm.PackageManager; import androidx.test.core.app.ApplicationProvider; +import com.android.server.appsearch.external.localstorage.util.PrefixUtil; + import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -78,13 +80,9 @@ public class VisibilityStoreTest { @Test public void testValidPackageName() { assertThat(VisibilityStore.PACKAGE_NAME) - .doesNotContain( - "" + AppSearchImpl.PACKAGE_DELIMITER); // Convert the chars to CharSequences + .doesNotContain(String.valueOf(PrefixUtil.PACKAGE_DELIMITER)); assertThat(VisibilityStore.PACKAGE_NAME) - .doesNotContain( - "" - + AppSearchImpl - .DATABASE_DELIMITER); // Convert the chars to CharSequences + .doesNotContain(String.valueOf(PrefixUtil.DATABASE_DELIMITER)); } /** @@ -93,13 +91,9 @@ public class VisibilityStoreTest { @Test public void testValidDatabaseName() { assertThat(VisibilityStore.DATABASE_NAME) - .doesNotContain( - "" + AppSearchImpl.PACKAGE_DELIMITER); // Convert the chars to CharSequences + .doesNotContain(String.valueOf(PrefixUtil.PACKAGE_DELIMITER)); assertThat(VisibilityStore.DATABASE_NAME) - .doesNotContain( - "" - + AppSearchImpl - .DATABASE_DELIMITER); // Convert the chars to CharSequences + .doesNotContain(String.valueOf(PrefixUtil.DATABASE_DELIMITER)); } @Test 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 ba4d5856b247..380d9be5ae57 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 @@ -16,6 +16,10 @@ package com.android.server.appsearch.external.localstorage; +import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.addPrefixToDocument; +import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix; +import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.removePrefixesFromDocument; + import static com.google.common.truth.Truth.assertThat; import static org.testng.Assert.expectThrows; @@ -35,8 +39,10 @@ import android.util.ArraySet; import androidx.test.core.app.ApplicationProvider; import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter; +import com.android.server.appsearch.external.localstorage.util.PrefixUtil; import com.android.server.appsearch.proto.DocumentProto; import com.android.server.appsearch.proto.GetOptimizeInfoResultProto; +import com.android.server.appsearch.proto.PersistType; import com.android.server.appsearch.proto.PropertyConfigProto; import com.android.server.appsearch.proto.PropertyProto; import com.android.server.appsearch.proto.SchemaProto; @@ -47,6 +53,7 @@ import com.android.server.appsearch.proto.StringIndexingConfig; import com.android.server.appsearch.proto.TermMatchType; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import org.junit.Before; @@ -54,6 +61,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -96,56 +104,71 @@ public class AppSearchImplTest { // Create a copy so we can modify it. List<SchemaTypeConfigProto> existingTypes = new ArrayList<>(existingSchemaBuilder.getTypesList()); - - SchemaProto newSchema = - SchemaProto.newBuilder() - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build()) - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("TestType") - .addProperties( - PropertyConfigProto.newBuilder() - .setPropertyName("subject") - .setDataType( - PropertyConfigProto.DataType.Code - .STRING) - .setCardinality( - PropertyConfigProto.Cardinality.Code - .OPTIONAL) - .setStringIndexingConfig( - StringIndexingConfig.newBuilder() - .setTokenizerType( - StringIndexingConfig - .TokenizerType - .Code.PLAIN) - .setTermMatchType( - TermMatchType.Code - .PREFIX) - .build()) - .build()) - .addProperties( - PropertyConfigProto.newBuilder() - .setPropertyName("link") - .setDataType( - PropertyConfigProto.DataType.Code - .DOCUMENT) - .setCardinality( - PropertyConfigProto.Cardinality.Code - .OPTIONAL) - .setSchemaType("RefType") + SchemaTypeConfigProto schemaTypeConfigProto1 = + SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build(); + SchemaTypeConfigProto schemaTypeConfigProto2 = + SchemaTypeConfigProto.newBuilder() + .setSchemaType("TestType") + .addProperties( + PropertyConfigProto.newBuilder() + .setPropertyName("subject") + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setCardinality( + PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setStringIndexingConfig( + StringIndexingConfig.newBuilder() + .setTokenizerType( + StringIndexingConfig.TokenizerType + .Code.PLAIN) + .setTermMatchType(TermMatchType.Code.PREFIX) .build()) .build()) + .addProperties( + PropertyConfigProto.newBuilder() + .setPropertyName("link") + .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT) + .setCardinality( + PropertyConfigProto.Cardinality.Code.OPTIONAL) + .setSchemaType("RefType") + .build()) + .build(); + SchemaTypeConfigProto schemaTypeConfigProto3 = + SchemaTypeConfigProto.newBuilder().setSchemaType("RefType").build(); + SchemaProto newSchema = + SchemaProto.newBuilder() + .addTypes(schemaTypeConfigProto1) + .addTypes(schemaTypeConfigProto2) + .addTypes(schemaTypeConfigProto3) .build(); AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema( - AppSearchImpl.createPrefix("package", "newDatabase"), - existingSchemaBuilder, - newSchema); + createPrefix("package", "newDatabase"), existingSchemaBuilder, newSchema); // We rewrote all the new types that were added. And nothing was removed. - assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes) - .containsExactly("package$newDatabase/Foo", "package$newDatabase/TestType"); + assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet()) + .containsExactly( + "package$newDatabase/Foo", + "package$newDatabase/TestType", + "package$newDatabase/RefType"); + assertThat( + rewrittenSchemaResults + .mRewrittenPrefixedTypes + .get("package$newDatabase/Foo") + .getSchemaType()) + .isEqualTo("package$newDatabase/Foo"); + assertThat( + rewrittenSchemaResults + .mRewrittenPrefixedTypes + .get("package$newDatabase/TestType") + .getSchemaType()) + .isEqualTo("package$newDatabase/TestType"); + assertThat( + rewrittenSchemaResults + .mRewrittenPrefixedTypes + .get("package$newDatabase/RefType") + .getSchemaType()) + .isEqualTo("package$newDatabase/RefType"); assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty(); SchemaProto expectedSchema = @@ -190,6 +213,10 @@ public class AppSearchImplTest { "package$newDatabase/RefType") .build()) .build()) + .addTypes( + SchemaTypeConfigProto.newBuilder() + .setSchemaType("package$newDatabase/RefType") + .build()) .build(); existingTypes.addAll(expectedSchema.getTypesList()); @@ -216,12 +243,12 @@ public class AppSearchImplTest { AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema( - AppSearchImpl.createPrefix("package", "existingDatabase"), + createPrefix("package", "existingDatabase"), existingSchemaBuilder, newSchema); // Nothing was removed, but the method did rewrite the type name. - assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes) + assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet()) .containsExactly("package$existingDatabase/Foo"); assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty(); @@ -251,14 +278,15 @@ public class AppSearchImplTest { AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema( - AppSearchImpl.createPrefix("package", "existingDatabase"), + createPrefix("package", "existingDatabase"), existingSchemaBuilder, newSchema); // Bar type was rewritten, but Foo ended up being deleted since it wasn't included in the // new schema. assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes) - .containsExactly("package$existingDatabase/Bar"); + .containsKey("package$existingDatabase/Bar"); + assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet().size()).isEqualTo(1); assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes) .containsExactly("package$existingDatabase/Foo"); @@ -308,8 +336,7 @@ public class AppSearchImplTest { .build(); DocumentProto.Builder actualDocument = documentProto.toBuilder(); - mAppSearchImpl.addPrefixToDocument( - actualDocument, AppSearchImpl.createPrefix("package", "databaseName")); + addPrefixToDocument(actualDocument, createPrefix("package", "databaseName")); assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto); } @@ -347,8 +374,7 @@ public class AppSearchImplTest { .build(); DocumentProto.Builder actualDocument = documentProto.toBuilder(); - assertThat(mAppSearchImpl.removePrefixesFromDocument(actualDocument)) - .isEqualTo("package$databaseName/"); + assertThat(removePrefixesFromDocument(actualDocument)).isEqualTo("package$databaseName/"); assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto); } @@ -365,8 +391,7 @@ public class AppSearchImplTest { DocumentProto.Builder actualDocument = documentProto.toBuilder(); AppSearchException e = expectThrows( - AppSearchException.class, - () -> mAppSearchImpl.removePrefixesFromDocument(actualDocument)); + AppSearchException.class, () -> removePrefixesFromDocument(actualDocument)); assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names"); } @@ -391,8 +416,7 @@ public class AppSearchImplTest { DocumentProto.Builder actualDocument = documentProto.toBuilder(); AppSearchException e = expectThrows( - AppSearchException.class, - () -> mAppSearchImpl.removePrefixesFromDocument(actualDocument)); + AppSearchException.class, () -> removePrefixesFromDocument(actualDocument)); assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names"); } @@ -484,7 +508,7 @@ public class AppSearchImplTest { // Rewrite SearchSpec mAppSearchImpl.rewriteSearchSpecForPrefixesLocked( searchSpecProto, - Collections.singleton(AppSearchImpl.createPrefix("package", "database")), + Collections.singleton(createPrefix("package", "database")), ImmutableSet.of("package$database/type")); assertThat(searchSpecProto.getSchemaTypeFiltersList()) .containsExactly("package$database/type"); @@ -531,8 +555,7 @@ public class AppSearchImplTest { mAppSearchImpl.rewriteSearchSpecForPrefixesLocked( searchSpecProto, ImmutableSet.of( - AppSearchImpl.createPrefix("package", "database1"), - AppSearchImpl.createPrefix("package", "database2")), + createPrefix("package", "database1"), createPrefix("package", "database2")), ImmutableSet.of( "package$database1/typeA", "package$database1/typeB", "package$database2/typeA", "package$database2/typeB")); @@ -573,8 +596,7 @@ public class AppSearchImplTest { assertThat( mAppSearchImpl.rewriteSearchSpecForPrefixesLocked( searchSpecProto, - Collections.singleton( - AppSearchImpl.createPrefix("package", "database")), + Collections.singleton(createPrefix("package", "database")), /*allowedPrefixedSchemas=*/ Collections.emptySet())) .isFalse(); } @@ -1082,7 +1104,7 @@ public class AppSearchImplTest { // Has database1 Set<String> expectedPrefixes = new ArraySet<>(existingPrefixes); - expectedPrefixes.add(AppSearchImpl.createPrefix("package", "database1")); + expectedPrefixes.add(createPrefix("package", "database1")); mAppSearchImpl.setSchema( "package", "database1", @@ -1094,7 +1116,7 @@ public class AppSearchImplTest { assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactlyElementsIn(expectedPrefixes); // Has both databases - expectedPrefixes.add(AppSearchImpl.createPrefix("package", "database2")); + expectedPrefixes.add(createPrefix("package", "database2")); mAppSearchImpl.setSchema( "package", "database2", @@ -1110,9 +1132,9 @@ public class AppSearchImplTest { public void testRewriteSearchResultProto() throws Exception { final String prefix = "com.package.foo" - + AppSearchImpl.PACKAGE_DELIMITER + + PrefixUtil.PACKAGE_DELIMITER + "databaseName" - + AppSearchImpl.DATABASE_DELIMITER; + + PrefixUtil.DATABASE_DELIMITER; final String uri = "uri"; final String namespace = prefix + "namespace"; final String schemaType = prefix + "schema"; @@ -1128,18 +1150,22 @@ public class AppSearchImplTest { SearchResultProto.ResultProto.newBuilder().setDocument(documentProto).build(); SearchResultProto searchResultProto = SearchResultProto.newBuilder().addResults(resultProto).build(); + SchemaTypeConfigProto schemaTypeConfigProto = + SchemaTypeConfigProto.newBuilder().setSchemaType(schemaType).build(); + Map<String, Map<String, SchemaTypeConfigProto>> schemaMap = + ImmutableMap.of(prefix, ImmutableMap.of(schemaType, schemaTypeConfigProto)); DocumentProto.Builder strippedDocumentProto = documentProto.toBuilder(); - AppSearchImpl.removePrefixesFromDocument(strippedDocumentProto); + removePrefixesFromDocument(strippedDocumentProto); SearchResultPage searchResultPage = - AppSearchImpl.rewriteSearchResultProto(searchResultProto); + AppSearchImpl.rewriteSearchResultProto(searchResultProto, schemaMap); for (SearchResult result : searchResultPage.getResults()) { assertThat(result.getPackageName()).isEqualTo("com.package.foo"); assertThat(result.getDatabaseName()).isEqualTo("databaseName"); assertThat(result.getGenericDocument()) .isEqualTo( GenericDocumentToProtoConverter.toGenericDocument( - strippedDocumentProto.build())); + strippedDocumentProto.build(), prefix, schemaMap.get(prefix))); } } @@ -1609,7 +1635,221 @@ public class AppSearchImplTest { expectThrows( IllegalStateException.class, () -> { - appSearchImpl.persistToDisk(); + appSearchImpl.persistToDisk(PersistType.Code.FULL); }); } + + @Test + public void testPutPersistsWithLiteFlush() throws Exception { + // Setup the index + Context context = ApplicationProvider.getApplicationContext(); + File appsearchDir = mTemporaryFolder.newFolder(); + AppSearchImpl appSearchImpl = + AppSearchImpl.create( + appsearchDir, + context, + VisibilityStore.NO_OP_USER_ID, + /*globalQuerierPackage=*/ ""); + + List<AppSearchSchema> schemas = + Collections.singletonList(new AppSearchSchema.Builder("type").build()); + appSearchImpl.setSchema( + "package", + "database", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*forceOverride=*/ false, + /*version=*/ 0); + + // Add a document and persist it. + GenericDocument document = + new GenericDocument.Builder<>("namespace1", "uri1", "type").build(); + appSearchImpl.putDocument("package", "database", document, /*logger=*/ null); + appSearchImpl.persistToDisk(PersistType.Code.LITE); + + GenericDocument getResult = + appSearchImpl.getDocument( + "package", "database", "namespace1", "uri1", Collections.emptyMap()); + assertThat(getResult).isEqualTo(document); + + // That document should be visible even from another instance. + AppSearchImpl appSearchImpl2 = + AppSearchImpl.create( + appsearchDir, + context, + VisibilityStore.NO_OP_USER_ID, + /*globalQuerierPackage=*/ ""); + getResult = + appSearchImpl2.getDocument( + "package", "database", "namespace1", "uri1", Collections.emptyMap()); + assertThat(getResult).isEqualTo(document); + } + + @Test + public void testDeletePersistsWithLiteFlush() throws Exception { + // Setup the index + Context context = ApplicationProvider.getApplicationContext(); + File appsearchDir = mTemporaryFolder.newFolder(); + AppSearchImpl appSearchImpl = + AppSearchImpl.create( + appsearchDir, + context, + VisibilityStore.NO_OP_USER_ID, + /*globalQuerierPackage=*/ ""); + + List<AppSearchSchema> schemas = + Collections.singletonList(new AppSearchSchema.Builder("type").build()); + appSearchImpl.setSchema( + "package", + "database", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*forceOverride=*/ false, + /*version=*/ 0); + + // Add two documents and persist them. + GenericDocument document1 = + new GenericDocument.Builder<>("namespace1", "uri1", "type").build(); + appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null); + GenericDocument document2 = + new GenericDocument.Builder<>("namespace1", "uri2", "type").build(); + appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null); + appSearchImpl.persistToDisk(PersistType.Code.LITE); + + GenericDocument getResult = + appSearchImpl.getDocument( + "package", "database", "namespace1", "uri1", Collections.emptyMap()); + assertThat(getResult).isEqualTo(document1); + getResult = + appSearchImpl.getDocument( + "package", "database", "namespace1", "uri2", Collections.emptyMap()); + assertThat(getResult).isEqualTo(document2); + + // Delete the first document + appSearchImpl.remove("package", "database", "namespace1", "uri1"); + appSearchImpl.persistToDisk(PersistType.Code.LITE); + expectThrows( + AppSearchException.class, + () -> + appSearchImpl.getDocument( + "package", + "database", + "namespace1", + "uri1", + Collections.emptyMap())); + getResult = + appSearchImpl.getDocument( + "package", "database", "namespace1", "uri2", Collections.emptyMap()); + assertThat(getResult).isEqualTo(document2); + + // Only the second document should be retrievable from another instance. + AppSearchImpl appSearchImpl2 = + AppSearchImpl.create( + appsearchDir, + context, + VisibilityStore.NO_OP_USER_ID, + /*globalQuerierPackage=*/ ""); + expectThrows( + AppSearchException.class, + () -> + appSearchImpl2.getDocument( + "package", + "database", + "namespace1", + "uri1", + Collections.emptyMap())); + getResult = + appSearchImpl2.getDocument( + "package", "database", "namespace1", "uri2", Collections.emptyMap()); + assertThat(getResult).isEqualTo(document2); + } + + @Test + public void testDeleteByQueryPersistsWithLiteFlush() throws Exception { + // Setup the index + Context context = ApplicationProvider.getApplicationContext(); + File appsearchDir = mTemporaryFolder.newFolder(); + AppSearchImpl appSearchImpl = + AppSearchImpl.create( + appsearchDir, + context, + VisibilityStore.NO_OP_USER_ID, + /*globalQuerierPackage=*/ ""); + + List<AppSearchSchema> schemas = + Collections.singletonList(new AppSearchSchema.Builder("type").build()); + appSearchImpl.setSchema( + "package", + "database", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*schemasPackageAccessible=*/ Collections.emptyMap(), + /*forceOverride=*/ false, + /*version=*/ 0); + + // Add two documents and persist them. + GenericDocument document1 = + new GenericDocument.Builder<>("namespace1", "uri1", "type").build(); + appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null); + GenericDocument document2 = + new GenericDocument.Builder<>("namespace2", "uri2", "type").build(); + appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null); + appSearchImpl.persistToDisk(PersistType.Code.LITE); + + GenericDocument getResult = + appSearchImpl.getDocument( + "package", "database", "namespace1", "uri1", Collections.emptyMap()); + assertThat(getResult).isEqualTo(document1); + getResult = + appSearchImpl.getDocument( + "package", "database", "namespace2", "uri2", Collections.emptyMap()); + assertThat(getResult).isEqualTo(document2); + + // Delete the first document + appSearchImpl.removeByQuery( + "package", + "database", + "", + new SearchSpec.Builder() + .addFilterNamespaces("namespace1") + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + appSearchImpl.persistToDisk(PersistType.Code.LITE); + expectThrows( + AppSearchException.class, + () -> + appSearchImpl.getDocument( + "package", + "database", + "namespace1", + "uri1", + Collections.emptyMap())); + getResult = + appSearchImpl.getDocument( + "package", "database", "namespace2", "uri2", Collections.emptyMap()); + assertThat(getResult).isEqualTo(document2); + + // Only the second document should be retrievable from another instance. + AppSearchImpl appSearchImpl2 = + AppSearchImpl.create( + appsearchDir, + context, + VisibilityStore.NO_OP_USER_ID, + /*globalQuerierPackage=*/ ""); + expectThrows( + AppSearchException.class, + () -> + appSearchImpl2.getDocument( + "package", + "database", + "namespace1", + "uri1", + Collections.emptyMap())); + getResult = + appSearchImpl2.getDocument( + "package", "database", "namespace2", "uri2", Collections.emptyMap()); + assertThat(getResult).isEqualTo(document2); + } } 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 70e1e05174ef..63f031722ede 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 @@ -21,35 +21,50 @@ import static com.google.common.truth.Truth.assertThat; import android.app.appsearch.GenericDocument; import com.android.server.appsearch.proto.DocumentProto; +import com.android.server.appsearch.proto.PropertyConfigProto; import com.android.server.appsearch.proto.PropertyProto; +import com.android.server.appsearch.proto.SchemaTypeConfigProto; import com.android.server.appsearch.protobuf.ByteString; +import com.google.common.collect.ImmutableMap; + import org.junit.Test; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; public class GenericDocumentToProtoConverterTest { private static final byte[] BYTE_ARRAY_1 = new byte[] {(byte) 1, (byte) 2, (byte) 3}; private static final byte[] BYTE_ARRAY_2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7}; + private static final String SCHEMA_TYPE_1 = "sDocumentPropertiesSchemaType1"; + private static final String SCHEMA_TYPE_2 = "sDocumentPropertiesSchemaType2"; private static final GenericDocument DOCUMENT_PROPERTIES_1 = new GenericDocument.Builder<GenericDocument.Builder<?>>( - "namespace", "sDocumentProperties1", "sDocumentPropertiesSchemaType1") + "namespace", "sDocumentProperties1", SCHEMA_TYPE_1) .setCreationTimestampMillis(12345L) .build(); private static final GenericDocument DOCUMENT_PROPERTIES_2 = new GenericDocument.Builder<GenericDocument.Builder<?>>( - "namespace", "sDocumentProperties2", "sDocumentPropertiesSchemaType2") + "namespace", "sDocumentProperties2", SCHEMA_TYPE_2) .setCreationTimestampMillis(6789L) .build(); + private static final SchemaTypeConfigProto SCHEMA_PROTO_1 = + SchemaTypeConfigProto.newBuilder().setSchemaType(SCHEMA_TYPE_1).build(); + private static final SchemaTypeConfigProto SCHEMA_PROTO_2 = + SchemaTypeConfigProto.newBuilder().setSchemaType(SCHEMA_TYPE_2).build(); + private static final String PREFIX = "package$databaseName/"; + private static final Map<String, SchemaTypeConfigProto> SCHEMA_MAP = + ImmutableMap.of( + PREFIX + SCHEMA_TYPE_1, SCHEMA_PROTO_1, PREFIX + SCHEMA_TYPE_2, SCHEMA_PROTO_2); @Test public void testDocumentProtoConvert() { GenericDocument document = new GenericDocument.Builder<GenericDocument.Builder<?>>( - "namespace", "uri1", "schemaType1") + "namespace", "uri1", SCHEMA_TYPE_1) .setCreationTimestampMillis(5L) .setScore(1) .setTtlMillis(1L) @@ -66,7 +81,7 @@ public class GenericDocumentToProtoConverterTest { DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder() .setUri("uri1") - .setSchema("schemaType1") + .setSchema(SCHEMA_TYPE_1) .setCreationTimestampMs(5L) .setScore(1) .setTtlMs(1L) @@ -109,9 +124,133 @@ public class GenericDocumentToProtoConverterTest { documentProtoBuilder.addProperties(propertyProtoMap.get(key)); } DocumentProto documentProto = documentProtoBuilder.build(); - assertThat(GenericDocumentToProtoConverter.toDocumentProto(document)) - .isEqualTo(documentProto); - assertThat(document) - .isEqualTo(GenericDocumentToProtoConverter.toGenericDocument(documentProto)); + + GenericDocument convertedGenericDocument = + GenericDocumentToProtoConverter.toGenericDocument( + documentProto, PREFIX, SCHEMA_MAP); + DocumentProto convertedDocumentProto = + GenericDocumentToProtoConverter.toDocumentProto(document); + + assertThat(convertedDocumentProto).isEqualTo(documentProto); + assertThat(convertedGenericDocument).isEqualTo(document); + } + + @Test + public void testConvertDocument_whenPropertyHasEmptyList() { + String emptyStringPropertyName = "emptyStringProperty"; + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri("uri1") + .setSchema(SCHEMA_TYPE_1) + .setCreationTimestampMs(5L) + .setNamespace("namespace") + .addProperties( + PropertyProto.newBuilder().setName(emptyStringPropertyName).build()) + .build(); + + PropertyConfigProto emptyStringListProperty = + PropertyConfigProto.newBuilder() + .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED) + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setPropertyName(emptyStringPropertyName) + .build(); + SchemaTypeConfigProto schemaTypeConfigProto = + SchemaTypeConfigProto.newBuilder() + .addProperties(emptyStringListProperty) + .setSchemaType(SCHEMA_TYPE_1) + .build(); + Map<String, SchemaTypeConfigProto> schemaMap = + ImmutableMap.of(PREFIX + SCHEMA_TYPE_1, schemaTypeConfigProto); + + GenericDocument convertedDocument = + GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX, schemaMap); + + GenericDocument expectedDocument = + new GenericDocument.Builder<GenericDocument.Builder<?>>( + "namespace", "uri1", SCHEMA_TYPE_1) + .setCreationTimestampMillis(5L) + .setPropertyString(emptyStringPropertyName) + .build(); + assertThat(convertedDocument).isEqualTo(expectedDocument); + assertThat(expectedDocument.getPropertyStringArray(emptyStringPropertyName)).isEmpty(); + } + + @Test + public void testConvertDocument_whenNestedDocumentPropertyHasEmptyList() { + String emptyStringPropertyName = "emptyStringProperty"; + String documentPropertyName = "documentProperty"; + DocumentProto nestedDocumentProto = + DocumentProto.newBuilder() + .setUri("uri2") + .setSchema(SCHEMA_TYPE_2) + .setCreationTimestampMs(5L) + .setNamespace("namespace") + .addProperties( + PropertyProto.newBuilder().setName(emptyStringPropertyName).build()) + .build(); + DocumentProto documentProto = + DocumentProto.newBuilder() + .setUri("uri1") + .setSchema(SCHEMA_TYPE_1) + .setCreationTimestampMs(5L) + .setNamespace("namespace") + .addProperties( + PropertyProto.newBuilder() + .addDocumentValues(nestedDocumentProto) + .setName(documentPropertyName) + .build()) + .build(); + + PropertyConfigProto documentProperty = + PropertyConfigProto.newBuilder() + .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED) + .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT) + .setPropertyName(documentPropertyName) + .setSchemaType(SCHEMA_TYPE_2) + .build(); + SchemaTypeConfigProto schemaTypeConfigProto = + SchemaTypeConfigProto.newBuilder() + .addProperties(documentProperty) + .setSchemaType(SCHEMA_TYPE_1) + .build(); + PropertyConfigProto emptyStringListProperty = + PropertyConfigProto.newBuilder() + .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED) + .setDataType(PropertyConfigProto.DataType.Code.STRING) + .setPropertyName(emptyStringPropertyName) + .build(); + SchemaTypeConfigProto nestedSchemaTypeConfigProto = + SchemaTypeConfigProto.newBuilder() + .addProperties(emptyStringListProperty) + .setSchemaType(SCHEMA_TYPE_2) + .build(); + Map<String, SchemaTypeConfigProto> schemaMap = + ImmutableMap.of( + PREFIX + SCHEMA_TYPE_1, + schemaTypeConfigProto, + PREFIX + SCHEMA_TYPE_2, + nestedSchemaTypeConfigProto); + + GenericDocument convertedDocument = + GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX, schemaMap); + + GenericDocument expectedDocument = + new GenericDocument.Builder<GenericDocument.Builder<?>>( + "namespace", "uri1", SCHEMA_TYPE_1) + .setCreationTimestampMillis(5L) + .setPropertyDocument( + documentPropertyName, + new GenericDocument.Builder<GenericDocument.Builder<?>>( + "namespace", "uri2", SCHEMA_TYPE_2) + .setCreationTimestampMillis(5L) + .setPropertyString(emptyStringPropertyName) + .build()) + .build(); + assertThat(convertedDocument).isEqualTo(expectedDocument); + assertThat( + expectedDocument + .getPropertyDocument(documentPropertyName) + .getPropertyStringArray(emptyStringPropertyName)) + .isEmpty(); } } 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 d07211fe2028..26fac492ccd2 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 @@ -21,8 +21,10 @@ import static com.google.common.truth.Truth.assertThat; import android.app.appsearch.SearchResult; import android.app.appsearch.SearchResultPage; +import com.android.server.appsearch.external.localstorage.util.PrefixUtil; import com.android.server.appsearch.proto.DocumentProto; import com.android.server.appsearch.proto.PropertyProto; +import com.android.server.appsearch.proto.SchemaTypeConfigProto; import com.android.server.appsearch.proto.SearchResultProto; import com.android.server.appsearch.proto.SnippetMatchProto; import com.android.server.appsearch.proto.SnippetProto; @@ -30,20 +32,29 @@ import com.android.server.appsearch.proto.SnippetProto; import org.junit.Test; import java.util.Collections; +import java.util.Map; public class SnippetTest { + private static final String SCHEMA_TYPE = "schema1"; + private static final String PACKAGE_NAME = "packageName"; + private static final String DATABASE_NAME = "databaseName"; + private static final String PREFIX = PrefixUtil.createPrefix(PACKAGE_NAME, DATABASE_NAME); + private static final SchemaTypeConfigProto SCHEMA_TYPE_CONFIG_PROTO = + SchemaTypeConfigProto.newBuilder().setSchemaType(PREFIX + SCHEMA_TYPE).build(); + private static final Map<String, Map<String, SchemaTypeConfigProto>> SCHEMA_MAP = + Collections.singletonMap( + PREFIX, + Collections.singletonMap(PREFIX + SCHEMA_TYPE, SCHEMA_TYPE_CONFIG_PROTO)); // TODO(tytytyww): Add tests for Double and Long Snippets. @Test public void testSingleStringSnippet() { - 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 schemaType = "schema1"; final String searchWord = "foo"; final String exactMatch = "foo"; final String window = "is foo"; @@ -57,7 +68,7 @@ public class SnippetTest { DocumentProto documentProto = DocumentProto.newBuilder() .setUri(uri) - .setSchema(schemaType) + .setSchema(SCHEMA_TYPE) .addProperties(property) .build(); SnippetProto snippetProto = @@ -86,8 +97,9 @@ public class SnippetTest { SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage( searchResultProto, - Collections.singletonList("packageName"), - Collections.singletonList("databaseName")); + 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); @@ -112,7 +124,6 @@ public class SnippetTest { + " Another nonsense word that’s used a lot\n" + " is bar.\n"; final String uri = "uri1"; - final String schemaType = "schema1"; final String searchWord = "foo"; final String exactMatch = "foo"; final String window = "is foo"; @@ -126,7 +137,7 @@ public class SnippetTest { DocumentProto documentProto = DocumentProto.newBuilder() .setUri(uri) - .setSchema(schemaType) + .setSchema(SCHEMA_TYPE) .addProperties(property) .build(); SearchResultProto.ResultProto resultProto = @@ -137,8 +148,9 @@ public class SnippetTest { SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage( searchResultProto, - Collections.singletonList("packageName"), - Collections.singletonList("databaseName")); + Collections.singletonList(PACKAGE_NAME), + Collections.singletonList(DATABASE_NAME), + SCHEMA_MAP); for (SearchResult result : searchResultPage.getResults()) { assertThat(result.getMatches()).isEmpty(); } @@ -162,7 +174,7 @@ public class SnippetTest { DocumentProto documentProto = DocumentProto.newBuilder() .setUri("uri1") - .setSchema("schema1") + .setSchema(SCHEMA_TYPE) .addProperties(property1) .addProperties(property2) .build(); @@ -203,8 +215,9 @@ public class SnippetTest { SearchResultPage searchResultPage = SearchResultToProtoConverter.toSearchResultPage( searchResultProto, - Collections.singletonList("packageName"), - Collections.singletonList("databaseName")); + Collections.singletonList(PACKAGE_NAME), + Collections.singletonList(DATABASE_NAME), + SCHEMA_MAP); for (SearchResult result : searchResultPage.getResults()) { SearchResult.MatchInfo match1 = result.getMatches().get(0); |