diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index 5fd45eadbda97..442ca7b8639b1 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -224,7 +224,11 @@ public class AppSearchManager { } AndroidFuture future = new AndroidFuture<>(); try { - mService.setSchema(DEFAULT_DATABASE_NAME, schemaBundles, request.isForceOverride(), + mService.setSchema( + DEFAULT_DATABASE_NAME, + schemaBundles, + new ArrayList<>(request.getSchemasNotPlatformSurfaceable()), + request.isForceOverride(), new IAppSearchResultCallback.Stub() { public void onResult(AppSearchResult result) { future.complete(result); diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java index 2db74a8fb7985..62cf38bca44db 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSchema.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.appsearch.exceptions.IllegalSchemaException; +import android.app.appsearch.util.BundleUtil; import android.os.Bundle; import android.util.ArraySet; @@ -31,6 +32,7 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.Set; /** @@ -40,7 +42,7 @@ import java.util.Set; * *

The schema consists of type information, properties, and config (like tokenization type). * - * @see AppSearchManager#setSchema + * @see AppSearchSession#setSchema */ public final class AppSearchSchema { private static final String SCHEMA_TYPE_FIELD = "schemaType"; @@ -94,17 +96,37 @@ public final class AppSearchSchema { return ret; } + @Override + public boolean equals(@Nullable Object other) { + if (this == other) { + return true; + } + if (!(other instanceof AppSearchSchema)) { + return false; + } + AppSearchSchema otherSchema = (AppSearchSchema) other; + if (!getSchemaType().equals(otherSchema.getSchemaType())) { + return false; + } + return getProperties().equals(otherSchema.getProperties()); + } + + @Override + public int hashCode() { + return Objects.hash(getSchemaType(), getProperties()); + } + /** Builder for {@link AppSearchSchema objects}. */ public static final class Builder { - private final String mTypeName; + private final String mSchemaType; private final ArrayList mPropertyBundles = new ArrayList<>(); private final Set mPropertyNames = new ArraySet<>(); private boolean mBuilt = false; /** Creates a new {@link AppSearchSchema.Builder}. */ - public Builder(@NonNull String typeName) { - Preconditions.checkNotNull(typeName); - mTypeName = typeName; + public Builder(@NonNull String schemaType) { + Preconditions.checkNotNull(schemaType); + mSchemaType = schemaType; } /** Adds a property to the given type. */ @@ -133,7 +155,7 @@ public final class AppSearchSchema { public AppSearchSchema build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); Bundle bundle = new Bundle(); - bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mTypeName); + bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mSchemaType); bundle.putParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD, mPropertyBundles); mBuilt = true; return new AppSearchSchema(bundle); @@ -279,6 +301,8 @@ public final class AppSearchSchema { final Bundle mBundle; + @Nullable private Integer mHashCode; + PropertyConfig(@NonNull Bundle bundle) { mBundle = Preconditions.checkNotNull(bundle); } @@ -327,6 +351,26 @@ public final class AppSearchSchema { return mBundle.getInt(TOKENIZER_TYPE_FIELD); } + @Override + public boolean equals(@Nullable Object other) { + if (this == other) { + return true; + } + if (!(other instanceof PropertyConfig)) { + return false; + } + PropertyConfig otherProperty = (PropertyConfig) other; + return BundleUtil.deepEquals(this.mBundle, otherProperty.mBundle); + } + + @Override + public int hashCode() { + if (mHashCode == null) { + mHashCode = BundleUtil.deepHashCode(mBundle); + } + return mHashCode; + } + /** * Builder for {@link PropertyConfig}. * diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index 9c7ccea4c43b5..b7cd4f5f8bce2 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java @@ -140,7 +140,11 @@ public final class AppSearchSession { schemaBundles.add(schema.getBundle()); } try { - mService.setSchema(mDatabaseName, schemaBundles, request.isForceOverride(), + mService.setSchema( + mDatabaseName, + schemaBundles, + new ArrayList<>(request.getSchemasNotPlatformSurfaceable()), + request.isForceOverride(), new IAppSearchResultCallback.Stub() { public void onResult(AppSearchResult result) { executor.execute(() -> callback.accept(result)); diff --git a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java index 0056377f007af..85207f7ed9d7f 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.appsearch.exceptions.AppSearchException; +import android.app.appsearch.util.BundleUtil; import android.os.Bundle; import android.util.Log; @@ -37,12 +38,12 @@ import java.util.Set; * *

Documents are constructed via {@link GenericDocument.Builder}. * - * @see AppSearchManager#putDocuments - * @see AppSearchManager#getByUri - * @see AppSearchManager#query + * @see AppSearchSession#putDocuments + * @see AppSearchSession#getByUri + * @see AppSearchSession#query */ public class GenericDocument { - private static final String TAG = "GenericDocument"; + private static final String TAG = "AppSearchGenericDocumen"; /** The default empty namespace. */ public static final String DEFAULT_NAMESPACE = ""; @@ -462,144 +463,17 @@ public class GenericDocument { return false; } GenericDocument otherDocument = (GenericDocument) other; - return bundleEquals(this.mBundle, otherDocument.mBundle); - } - - /** - * Deeply checks whether two bundles are equal. - * - *

Two bundles will be considered equal if they contain the same content. - */ - @SuppressWarnings("unchecked") - private static boolean bundleEquals(Bundle one, Bundle two) { - if (one.size() != two.size()) { - return false; - } - Set keySetOne = one.keySet(); - Object valueOne; - Object valueTwo; - // Bundle inherit its equals() from Object.java, which only compare their memory address. - // We should iterate all keys and check their presents and values in both bundle. - for (String key : keySetOne) { - valueOne = one.get(key); - valueTwo = two.get(key); - if (valueOne instanceof Bundle - && valueTwo instanceof Bundle - && !bundleEquals((Bundle) valueOne, (Bundle) valueTwo)) { - return false; - } else if (valueOne == null && (valueTwo != null || !two.containsKey(key))) { - // If we call bundle.get(key) when the 'key' doesn't actually exist in the - // bundle, we'll get back a null. So make sure that both values are null and - // both keys exist in the bundle. - return false; - } else if (valueOne instanceof boolean[]) { - if (!(valueTwo instanceof boolean[]) - || !Arrays.equals((boolean[]) valueOne, (boolean[]) valueTwo)) { - return false; - } - } else if (valueOne instanceof long[]) { - if (!(valueTwo instanceof long[]) - || !Arrays.equals((long[]) valueOne, (long[]) valueTwo)) { - return false; - } - } else if (valueOne instanceof double[]) { - if (!(valueTwo instanceof double[]) - || !Arrays.equals((double[]) valueOne, (double[]) valueTwo)) { - return false; - } - } else if (valueOne instanceof Bundle[]) { - if (!(valueTwo instanceof Bundle[])) { - return false; - } - Bundle[] bundlesOne = (Bundle[]) valueOne; - Bundle[] bundlesTwo = (Bundle[]) valueTwo; - if (bundlesOne.length != bundlesTwo.length) { - return false; - } - for (int i = 0; i < bundlesOne.length; i++) { - if (!bundleEquals(bundlesOne[i], bundlesTwo[i])) { - return false; - } - } - } else if (valueOne instanceof ArrayList) { - if (!(valueTwo instanceof ArrayList)) { - return false; - } - ArrayList bundlesOne = (ArrayList) valueOne; - ArrayList bundlesTwo = (ArrayList) valueTwo; - if (bundlesOne.size() != bundlesTwo.size()) { - return false; - } - for (int i = 0; i < bundlesOne.size(); i++) { - if (!bundleEquals(bundlesOne.get(i), bundlesTwo.get(i))) { - return false; - } - } - } else if (valueOne instanceof Object[]) { - if (!(valueTwo instanceof Object[]) - || !Arrays.equals((Object[]) valueOne, (Object[]) valueTwo)) { - return false; - } - } - } - return true; + return BundleUtil.deepEquals(this.mBundle, otherDocument.mBundle); } @Override public int hashCode() { if (mHashCode == null) { - mHashCode = bundleHashCode(mBundle); + mHashCode = BundleUtil.deepHashCode(mBundle); } return mHashCode; } - /** - * Calculates the hash code for a bundle. - * - *

The hash code is only effected by the contents in the bundle. Bundles will get consistent - * hash code if they have same contents. - */ - @SuppressWarnings("unchecked") - private static int bundleHashCode(Bundle bundle) { - int[] hashCodes = new int[bundle.size()]; - int i = 0; - // Bundle inherit its hashCode() from Object.java, which only relative to their memory - // address. Bundle doesn't have an order, so we should iterate all keys and combine - // their value's hashcode into an array. And use the hashcode of the array to be - // the hashcode of the bundle. - for (String key : bundle.keySet()) { - Object value = bundle.get(key); - if (value instanceof boolean[]) { - hashCodes[i++] = Arrays.hashCode((boolean[]) value); - } else if (value instanceof long[]) { - hashCodes[i++] = Arrays.hashCode((long[]) value); - } else if (value instanceof double[]) { - hashCodes[i++] = Arrays.hashCode((double[]) value); - } else if (value instanceof String[]) { - hashCodes[i++] = Arrays.hashCode((Object[]) value); - } else if (value instanceof Bundle) { - hashCodes[i++] = bundleHashCode((Bundle) value); - } else if (value instanceof Bundle[]) { - Bundle[] bundles = (Bundle[]) value; - int[] innerHashCodes = new int[bundles.length]; - for (int j = 0; j < innerHashCodes.length; j++) { - innerHashCodes[j] = bundleHashCode(bundles[j]); - } - hashCodes[i++] = Arrays.hashCode(innerHashCodes); - } else if (value instanceof ArrayList) { - ArrayList bundles = (ArrayList) value; - int[] innerHashCodes = new int[bundles.size()]; - for (int j = 0; j < innerHashCodes.length; j++) { - innerHashCodes[j] = bundleHashCode(bundles.get(j)); - } - hashCodes[i++] = Arrays.hashCode(innerHashCodes); - } else { - hashCodes[i++] = value.hashCode(); - } - } - return Arrays.hashCode(hashCodes); - } - @Override @NonNull public String toString() { @@ -683,10 +557,10 @@ public class GenericDocument { * * @param uri The uri of {@link GenericDocument}. * @param schemaType The schema type of the {@link GenericDocument}. The passed-in {@code - * schemaType} must be defined using {@link AppSearchManager#setSchema} prior to + * schemaType} must be defined using {@link AppSearchSession#setSchema} prior to * inserting a document of this {@code schemaType} into the AppSearch index using {@link - * AppSearchManager#putDocuments}. Otherwise, the document will be rejected by {@link - * AppSearchManager#putDocuments}. + * AppSearchSession#putDocuments}. Otherwise, the document will be rejected by {@link + * AppSearchSession#putDocuments}. */ @SuppressWarnings("unchecked") public Builder(@NonNull String uri, @NonNull String schemaType) { diff --git a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java index b1cf504843060..74afdd2c7a80b 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java @@ -29,7 +29,7 @@ import java.util.Set; /** * Encapsulates a request to retrieve documents by namespace and URI. * - * @see AppSearchManager#getByUri + * @see AppSearchSession#getByUri */ public final class GetByUriRequest { private final String mNamespace; diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl index 22e00f2cdfdc3..1d7cb87131c03 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl @@ -32,6 +32,8 @@ interface IAppSearchManager { * * @param databaseName The databaseName this document resides in. * @param schemaBundles List of AppSearchSchema bundles. + * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform + * surfaces. * @param forceOverride Whether to apply the new schema even if it is incompatible. All * incompatible documents will be deleted. * @param callback {@link IAppSearchResultCallback#onResult} will be called with an @@ -40,6 +42,7 @@ interface IAppSearchManager { void setSchema( in String databaseName, in List schemaBundles, + in List schemasNotPlatformSurfaceable, boolean forceOverride, in IAppSearchResultCallback callback); diff --git a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java index 1e37277be55eb..1c360a65a0417 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java @@ -29,9 +29,9 @@ import java.util.Collections; import java.util.List; /** - * Encapsulates a request to index a document into an {@link AppSearchManager} database. + * Encapsulates a request to index a document into an {@link AppSearchSession} database. * - * @see AppSearchManager#putDocuments + * @see AppSearchSession#putDocuments */ public final class PutDocumentsRequest { private final List mDocuments; diff --git a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java index 1c17547574a66..be6d15708d2e2 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java @@ -29,7 +29,7 @@ import java.util.Set; /** * Encapsulates a request to remove documents by namespace and URI. * - * @see AppSearchManager#removeByUri + * @see AppSearchSession#removeByUri */ public final class RemoveByUriRequest { private final String mNamespace; diff --git a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java index 081fb7202c2dc..0e031312f420a 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java @@ -26,27 +26,43 @@ import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Set; /** - * Encapsulates a request to update the schema of an {@link AppSearchManager} database. + * Encapsulates a request to update the schema of an {@link AppSearchSession} database. * - * @see AppSearchManager#setSchema + * @see AppSearchSession#setSchema */ public final class SetSchemaRequest { private final Set mSchemas; + private final Set mSchemasNotPlatformSurfaceable; private final boolean mForceOverride; - SetSchemaRequest(Set schemas, boolean forceOverride) { - mSchemas = schemas; + SetSchemaRequest( + @NonNull Set schemas, + @NonNull Set schemasNotPlatformSurfaceable, + boolean forceOverride) { + mSchemas = Preconditions.checkNotNull(schemas); + mSchemasNotPlatformSurfaceable = Preconditions.checkNotNull(schemasNotPlatformSurfaceable); mForceOverride = forceOverride; } /** Returns the schemas that are part of this request. */ @NonNull public Set getSchemas() { - return mSchemas; + return Collections.unmodifiableSet(mSchemas); + } + + /** + * Returns the set of schema types that have opted out of being visible on system UI surfaces. + * + * @hide + */ + @NonNull + public Set getSchemasNotPlatformSurfaceable() { + return Collections.unmodifiableSet(mSchemasNotPlatformSurfaceable); } /** Returns whether this request will force the schema to be overridden. */ @@ -57,17 +73,26 @@ public final class SetSchemaRequest { /** Builder for {@link SetSchemaRequest} objects. */ public static final class Builder { private final Set mSchemas = new ArraySet<>(); + private final Set mSchemasNotPlatformSurfaceable = new ArraySet<>(); private boolean mForceOverride = false; private boolean mBuilt = false; - /** Adds one or more types to the schema. */ + /** + * Adds one or more types to the schema. + * + *

Any documents of these types will be visible on system UI surfaces by default. + */ @NonNull public Builder addSchema(@NonNull AppSearchSchema... schemas) { Preconditions.checkNotNull(schemas); return addSchema(Arrays.asList(schemas)); } - /** Adds one or more types to the schema. */ + /** + * Adds one or more types to the schema. + * + *

Any documents of these types will be visible on system UI surfaces by default. + */ @NonNull public Builder addSchema(@NonNull Collection schemas) { Preconditions.checkState(!mBuilt, "Builder has already been used"); @@ -76,14 +101,44 @@ public final class SetSchemaRequest { return this; } + /** + * Sets visibility on system UI surfaces for schema types. + * + * @hide + */ + @NonNull + public Builder setSchemaTypeVisibilityForSystemUi( + boolean visible, @NonNull String... schemaTypes) { + Preconditions.checkNotNull(schemaTypes); + return this.setSchemaTypeVisibilityForSystemUi(visible, Arrays.asList(schemaTypes)); + } + + /** + * Sets visibility on system UI surfaces for schema types. + * + * @hide + */ + @NonNull + public Builder setSchemaTypeVisibilityForSystemUi( + boolean visible, @NonNull Collection schemaTypes) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(schemaTypes); + if (visible) { + mSchemasNotPlatformSurfaceable.removeAll(schemaTypes); + } else { + mSchemasNotPlatformSurfaceable.addAll(schemaTypes); + } + return this; + } + /** * Configures the {@link SetSchemaRequest} to delete any existing documents that don't * follow the new schema. * *

By default, this is {@code false} and schema incompatibility causes the {@link - * AppSearchManager#setSchema} call to fail. + * AppSearchSession#setSchema} call to fail. * - * @see AppSearchManager#setSchema + * @see AppSearchSession#setSchema */ @NonNull public Builder setForceOverride(boolean forceOverride) { @@ -91,12 +146,34 @@ public final class SetSchemaRequest { return this; } - /** Builds a new {@link SetSchemaRequest}. */ + /** + * Builds a new {@link SetSchemaRequest}. + * + * @throws IllegalArgumentException If schema types were referenced, but the corresponding + * {@link AppSearchSchema} was never added. + */ @NonNull public SetSchemaRequest build() { Preconditions.checkState(!mBuilt, "Builder has already been used"); mBuilt = true; - return new SetSchemaRequest(mSchemas, mForceOverride); + + // Verify that any schema types with visibility settings refer to a real schema. + // Create a copy because we're going to remove from the set for verification purposes. + Set schemasNotPlatformSurfaceableCopy = + new ArraySet<>(mSchemasNotPlatformSurfaceable); + for (AppSearchSchema schema : mSchemas) { + schemasNotPlatformSurfaceableCopy.remove(schema.getSchemaType()); + } + if (!schemasNotPlatformSurfaceableCopy.isEmpty()) { + // We still have schema types that weren't seen in our mSchemas set. This means + // there wasn't a corresponding AppSearchSchema. + throw new IllegalArgumentException( + "Schema types " + + schemasNotPlatformSurfaceableCopy + + " referenced, but were not added."); + } + + return new SetSchemaRequest(mSchemas, mSchemasNotPlatformSurfaceable, mForceOverride); } } } diff --git a/apex/appsearch/framework/java/android/app/appsearch/util/BundleUtil.java b/apex/appsearch/framework/java/android/app/appsearch/util/BundleUtil.java new file mode 100644 index 0000000000000..1b4d28401ea0c --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/util/BundleUtil.java @@ -0,0 +1,221 @@ +/* + * 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.util; + +import android.annotation.Nullable; +import android.os.Bundle; +import android.util.SparseArray; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Utilities for working with {@link android.os.Bundle}. + * + * @hide + */ +public final class BundleUtil { + private BundleUtil() {} + + /** + * Deeply checks two bundles are equal or not. + * + *

Two bundles will be considered equal if they contain the same keys, and each value is also + * equal. Bundle values are compared using deepEquals. + */ + public static boolean deepEquals(@Nullable Bundle one, @Nullable Bundle two) { + if (one == null && two == null) { + return true; + } + if (one == null || two == null) { + return false; + } + if (one.size() != two.size()) { + return false; + } + if (!one.keySet().equals(two.keySet())) { + return false; + } + // Bundle inherit its equals() from Object.java, which only compare their memory address. + // We should iterate all keys and check their presents and values in both bundle. + for (String key : one.keySet()) { + if (!bundleValueEquals(one.get(key), two.get(key))) { + return false; + } + } + return true; + } + + /** + * Deeply checks whether two values in a Bundle are equal or not. + * + *

Values of type Bundle are compared using {@link #deepEquals}. + */ + private static boolean bundleValueEquals(@Nullable Object one, @Nullable Object two) { + if (one == null && two == null) { + return true; + } + if (one == null || two == null) { + return false; + } + if (one.equals(two)) { + return true; + } + if (one instanceof Bundle && two instanceof Bundle) { + return deepEquals((Bundle) one, (Bundle) two); + } else if (one instanceof int[] && two instanceof int[]) { + return Arrays.equals((int[]) one, (int[]) two); + } else if (one instanceof byte[] && two instanceof byte[]) { + return Arrays.equals((byte[]) one, (byte[]) two); + } else if (one instanceof char[] && two instanceof char[]) { + return Arrays.equals((char[]) one, (char[]) two); + } else if (one instanceof long[] && two instanceof long[]) { + return Arrays.equals((long[]) one, (long[]) two); + } else if (one instanceof float[] && two instanceof float[]) { + return Arrays.equals((float[]) one, (float[]) two); + } else if (one instanceof short[] && two instanceof short[]) { + return Arrays.equals((short[]) one, (short[]) two); + } else if (one instanceof double[] && two instanceof double[]) { + return Arrays.equals((double[]) one, (double[]) two); + } else if (one instanceof boolean[] && two instanceof boolean[]) { + return Arrays.equals((boolean[]) one, (boolean[]) two); + } else if (one instanceof Object[] && two instanceof Object[]) { + Object[] arrayOne = (Object[]) one; + Object[] arrayTwo = (Object[]) two; + if (arrayOne.length != arrayTwo.length) { + return false; + } + if (Arrays.equals(arrayOne, arrayTwo)) { + return true; + } + for (int i = 0; i < arrayOne.length; i++) { + if (!bundleValueEquals(arrayOne[i], arrayTwo[i])) { + return false; + } + } + return true; + } else if (one instanceof ArrayList && two instanceof ArrayList) { + ArrayList listOne = (ArrayList) one; + ArrayList listTwo = (ArrayList) two; + if (listOne.size() != listTwo.size()) { + return false; + } + for (int i = 0; i < listOne.size(); i++) { + if (!bundleValueEquals(listOne.get(i), listTwo.get(i))) { + return false; + } + } + return true; + } else if (one instanceof SparseArray && two instanceof SparseArray) { + SparseArray arrayOne = (SparseArray) one; + SparseArray arrayTwo = (SparseArray) two; + if (arrayOne.size() != arrayTwo.size()) { + return false; + } + for (int i = 0; i < arrayOne.size(); i++) { + if (arrayOne.keyAt(i) != arrayTwo.keyAt(i) + || !bundleValueEquals(arrayOne.valueAt(i), arrayTwo.valueAt(i))) { + return false; + } + } + return true; + } + return false; + } + + /** + * Calculates the hash code for a bundle. + * + *

The hash code is only effected by the contents in the bundle. Bundles will get consistent + * hash code if they have same contents. + */ + public static int deepHashCode(@Nullable Bundle bundle) { + if (bundle == null) { + return 0; + } + int[] hashCodes = new int[bundle.size()]; + int i = 0; + // Bundle inherit its hashCode() from Object.java, which only relative to their memory + // address. Bundle doesn't have an order, so we should iterate all keys and combine + // their value's hashcode into an array. And use the hashcode of the array to be + // the hashcode of the bundle. + for (String key : bundle.keySet()) { + Object value = bundle.get(key); + if (value instanceof Bundle) { + hashCodes[i++] = deepHashCode((Bundle) value); + } else if (value instanceof int[]) { + hashCodes[i++] = Arrays.hashCode((int[]) value); + } else if (value instanceof byte[]) { + hashCodes[i++] = Arrays.hashCode((byte[]) value); + } else if (value instanceof char[]) { + hashCodes[i++] = Arrays.hashCode((char[]) value); + } else if (value instanceof long[]) { + hashCodes[i++] = Arrays.hashCode((long[]) value); + } else if (value instanceof float[]) { + hashCodes[i++] = Arrays.hashCode((float[]) value); + } else if (value instanceof short[]) { + hashCodes[i++] = Arrays.hashCode((short[]) value); + } else if (value instanceof double[]) { + hashCodes[i++] = Arrays.hashCode((double[]) value); + } else if (value instanceof boolean[]) { + hashCodes[i++] = Arrays.hashCode((boolean[]) value); + } else if (value instanceof String[]) { + // Optimization to avoid Object[] handler creating an inner array for common cases + hashCodes[i++] = Arrays.hashCode((String[]) value); + } else if (value instanceof Object[]) { + Object[] array = (Object[]) value; + int[] innerHashCodes = new int[array.length]; + for (int j = 0; j < array.length; j++) { + if (array[j] instanceof Bundle) { + innerHashCodes[j] = deepHashCode((Bundle) array[j]); + } else if (array[j] != null) { + innerHashCodes[j] = array[j].hashCode(); + } + } + hashCodes[i++] = Arrays.hashCode(innerHashCodes); + } else if (value instanceof ArrayList) { + ArrayList list = (ArrayList) value; + int[] innerHashCodes = new int[list.size()]; + for (int j = 0; j < innerHashCodes.length; j++) { + Object item = list.get(j); + if (item instanceof Bundle) { + innerHashCodes[j] = deepHashCode((Bundle) item); + } else if (item != null) { + innerHashCodes[j] = item.hashCode(); + } + } + hashCodes[i++] = Arrays.hashCode(innerHashCodes); + } else if (value instanceof SparseArray) { + SparseArray array = (SparseArray) value; + int[] innerHashCodes = new int[array.size() * 2]; + for (int j = 0; j < array.size(); j++) { + innerHashCodes[j * 2] = array.keyAt(j); + Object item = array.valueAt(j); + if (item instanceof Bundle) { + innerHashCodes[j * 2 + 1] = deepHashCode((Bundle) item); + } else if (item != null) { + innerHashCodes[j * 2 + 1] = item.hashCode(); + } + } + hashCodes[i++] = Arrays.hashCode(innerHashCodes); + } else { + hashCodes[i++] = value.hashCode(); + } + } + return Arrays.hashCode(hashCodes); + } +} 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 d5146dd75c3b7..551347c5c202e 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -33,15 +33,14 @@ import android.os.Bundle; import android.os.ParcelableException; import android.os.RemoteException; import android.os.UserHandle; -import android.util.ArraySet; import android.util.Log; import com.android.internal.util.Preconditions; import com.android.server.SystemService; import com.android.server.appsearch.external.localstorage.AppSearchImpl; +import java.util.ArrayList; import java.util.List; -import java.util.Set; /** * TODO(b/142567528): add comments when implement this class @@ -64,6 +63,7 @@ public class AppSearchManagerService extends SystemService { public void setSchema( @NonNull String databaseName, @NonNull List schemaBundles, + @NonNull List schemasNotPlatformSurfaceable, boolean forceOverride, @NonNull IAppSearchResultCallback callback) { Preconditions.checkNotNull(databaseName); @@ -73,13 +73,13 @@ public class AppSearchManagerService extends SystemService { int callingUserId = UserHandle.getUserId(callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - Set schemas = new ArraySet<>(schemaBundles.size()); + List schemas = new ArrayList<>(schemaBundles.size()); for (int i = 0; i < schemaBundles.size(); i++) { schemas.add(new AppSearchSchema(schemaBundles.get(i))); } AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); - impl.setSchema(databaseName, schemas, forceOverride); + impl.setSchema(databaseName, schemas, schemasNotPlatformSurfaceable, forceOverride); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(/*result=*/ null)); } catch (Throwable t) { 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 247089ba258b8..62b81d066c8b0 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 @@ -60,6 +60,7 @@ import com.google.android.icing.proto.SetSchemaResultProto; import com.google.android.icing.proto.StatusProto; import java.io.File; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -226,13 +227,16 @@ public final class AppSearchImpl { * * @param databaseName The name of the database where this schema lives. * @param schemas Schemas to set for this app. + * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform + * surfaces. * @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents * which do not comply with the new schema will be deleted. * @throws AppSearchException on IcingSearchEngine error. */ public void setSchema( @NonNull String databaseName, - @NonNull Set schemas, + @NonNull List schemas, + @NonNull List schemasNotPlatformSurfaceable, boolean forceOverride) throws AppSearchException { mReadWriteLock.writeLock().lock(); @@ -240,8 +244,9 @@ public final class AppSearchImpl { SchemaProto.Builder existingSchemaBuilder = getSchemaProtoLocked().toBuilder(); SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder(); - for (AppSearchSchema schema : schemas) { - SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema); + for (int i = 0; i < schemas.size(); i++) { + SchemaTypeConfigProto schemaTypeProto = + SchemaToProtoConverter.toSchemaTypeConfigProto(schemas.get(i)); newSchemaBuilder.addTypes(schemaTypeProto); } @@ -276,8 +281,16 @@ public final class AppSearchImpl { // Update derived data structures. mSchemaMapLocked.put(databaseName, rewrittenSchemaResults.mRewrittenQualifiedTypes); - mVisibilityStoreLocked.updateSchemas( - databaseName, rewrittenSchemaResults.mDeletedQualifiedTypes); + + String databasePrefix = getDatabasePrefix(databaseName); + Set qualifiedSchemasNotPlatformSurfaceable = + new ArraySet<>(schemasNotPlatformSurfaceable.size()); + for (int i = 0; i < schemasNotPlatformSurfaceable.size(); i++) { + qualifiedSchemasNotPlatformSurfaceable.add( + databasePrefix + schemasNotPlatformSurfaceable.get(i)); + } + mVisibilityStoreLocked.setVisibility( + databaseName, qualifiedSchemasNotPlatformSurfaceable); // Determine whether to schedule an immediate optimize. if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0 @@ -294,38 +307,55 @@ public final class AppSearchImpl { } /** - * Update the visibility settings for this app. + * Retrieves the AppSearch schema for this database. * - *

This method belongs to the mutate group + *

This method belongs to query group. * - * @param databaseName The name of the database where the visibility settings will apply. - * @param schemasHiddenFromPlatformSurfaces Schemas that should be hidden from platform surfaces - * @throws AppSearchException on IcingSearchEngine error + * @param databaseName The name of the database where this schema lives. + * @throws AppSearchException on IcingSearchEngine error. */ - public void setVisibility( - @NonNull String databaseName, @NonNull Set schemasHiddenFromPlatformSurfaces) - throws AppSearchException { - mReadWriteLock.writeLock().lock(); + @NonNull + public List getSchema(@NonNull String databaseName) throws AppSearchException { + SchemaProto fullSchema; + mReadWriteLock.readLock().lock(); try { - String databasePrefix = getDatabasePrefix(databaseName); - Set qualifiedSchemasHiddenFromPlatformSurface = - new ArraySet<>(schemasHiddenFromPlatformSurfaces.size()); - for (String schema : schemasHiddenFromPlatformSurfaces) { - Set existingSchemas = mSchemaMapLocked.get(databaseName); - if (existingSchemas == null || !existingSchemas.contains(databasePrefix + schema)) { - throw new AppSearchException( - AppSearchResult.RESULT_NOT_FOUND, - "Unknown schema(s): " - + schemasHiddenFromPlatformSurfaces - + " provided during setVisibility."); - } - qualifiedSchemasHiddenFromPlatformSurface.add(databasePrefix + schema); - } - mVisibilityStoreLocked.setVisibility( - databaseName, qualifiedSchemasHiddenFromPlatformSurface); + fullSchema = getSchemaProtoLocked(); } finally { - mReadWriteLock.writeLock().lock(); + mReadWriteLock.readLock().unlock(); } + + List result = new ArrayList<>(); + for (int i = 0; i < fullSchema.getTypesCount(); i++) { + String typeDatabase = getDatabaseName(fullSchema.getTypes(i).getSchemaType()); + if (!databaseName.equals(typeDatabase)) { + continue; + } + // Rewrite SchemaProto.types.schema_type + SchemaTypeConfigProto.Builder typeConfigBuilder = fullSchema.getTypes(i).toBuilder(); + String newSchemaType = + typeConfigBuilder.getSchemaType().substring(databaseName.length() + 1); + typeConfigBuilder.setSchemaType(newSchemaType); + + // Rewrite SchemaProto.types.properties.schema_type + for (int propertyIdx = 0; + propertyIdx < typeConfigBuilder.getPropertiesCount(); + propertyIdx++) { + PropertyConfigProto.Builder propertyConfigBuilder = + typeConfigBuilder.getProperties(propertyIdx).toBuilder(); + if (!propertyConfigBuilder.getSchemaType().isEmpty()) { + String newPropertySchemaType = + propertyConfigBuilder + .getSchemaType() + .substring(databaseName.length() + 1); + propertyConfigBuilder.setSchemaType(newPropertySchemaType); + typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder); + } + } + + AppSearchSchema schema = SchemaToProtoConverter.toAppSearchSchema(typeConfigBuilder); + result.add(schema); + } + return result; } /** @@ -340,7 +370,7 @@ public final class AppSearchImpl { public void putDocument(@NonNull String databaseName, @NonNull GenericDocument document) throws AppSearchException { DocumentProto.Builder documentBuilder = - GenericDocumentToProtoConverter.convert(document).toBuilder(); + GenericDocumentToProtoConverter.toDocumentProto(document).toBuilder(); addPrefixToDocument(documentBuilder, getDatabasePrefix(databaseName)); PutResultProto putResultProto; @@ -384,7 +414,7 @@ public final class AppSearchImpl { DocumentProto.Builder documentBuilder = getResultProto.getDocument().toBuilder(); removeDatabasesFromDocument(documentBuilder); - return GenericDocumentToProtoConverter.convert(documentBuilder.build()); + return GenericDocumentToProtoConverter.toGenericDocument(documentBuilder.build()); } /** @@ -969,7 +999,7 @@ public final class AppSearchImpl { resultsBuilder.setResults(i, resultBuilder); } } - return SearchResultToProtoConverter.convertToSearchResultPage(resultsBuilder); + return SearchResultToProtoConverter.toSearchResultPage(resultsBuilder); } @GuardedBy("mReadWriteLock") diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java index 47228221a1f50..0b68ebcbfd3f8 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/VisibilityStore.java @@ -53,19 +53,23 @@ import java.util.Set; class VisibilityStore { // Schema type for documents that hold AppSearch's metadata, e.g. visibility settings @VisibleForTesting static final String SCHEMA_TYPE = "Visibility"; + // Property that holds the list of platform-hidden schemas, as part of the visibility // settings. - @VisibleForTesting static final String PLATFORM_HIDDEN_PROPERTY = "platformHidden"; + @VisibleForTesting + static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable"; // Database name to prefix all visibility schemas and documents with. Special-cased to // minimize the chance of collision with a client-supplied database. + @VisibleForTesting static final String DATABASE_NAME = "$$__AppSearch__Database"; + // Namespace of documents that contain visibility settings private static final String NAMESPACE = "namespace"; private final AppSearchImpl mAppSearchImpl; // The map contains schemas that are platform-hidden for each database. All schemas in the map // have a database name prefix. - private final Map> mPlatformHiddenMap = new ArrayMap<>(); + private final Map> mNotPlatformSurfaceableMap = new ArrayMap<>(); /** * Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()} @@ -82,8 +86,8 @@ class VisibilityStore { * *

This is kept separate from the constructor because this will call methods on * AppSearchImpl. Some may even then recursively call back into VisibilityStore (for example, - * {@link AppSearchImpl#setSchema} will call {@link #updateSchemas}. We need to have both - * AppSearchImpl and VisibilityStore fully initialized for this call flow to work. + * {@link AppSearchImpl#setSchema} will call {@link #setVisibility(String, Set)}. We need to + * have both AppSearchImpl and VisibilityStore fully initialized for this call flow to work. * * @throws AppSearchException AppSearchException on AppSearchImpl error. */ @@ -92,11 +96,11 @@ class VisibilityStore { // Schema type doesn't exist yet. Add it. mAppSearchImpl.setSchema( DATABASE_NAME, - Collections.singleton( + Collections.singletonList( new AppSearchSchema.Builder(SCHEMA_TYPE) .addProperty( new AppSearchSchema.PropertyConfig.Builder( - PLATFORM_HIDDEN_PROPERTY) + NOT_PLATFORM_SURFACEABLE_PROPERTY) .setDataType( AppSearchSchema.PropertyConfig .DATA_TYPE_STRING) @@ -105,6 +109,7 @@ class VisibilityStore { .CARDINALITY_REPEATED) .build()) .build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*forceOverride=*/ false); } @@ -120,8 +125,9 @@ class VisibilityStore { GenericDocument document = mAppSearchImpl.getDocument(DATABASE_NAME, NAMESPACE, /*uri=*/ database); - String[] schemas = document.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY); - mPlatformHiddenMap.put(database, new ArraySet<>(Arrays.asList(schemas))); + String[] schemas = + document.getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY); + mNotPlatformSurfaceableMap.put(database, new ArraySet<>(Arrays.asList(schemas))); } catch (AppSearchException e) { if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) { // TODO(b/172068212): This indicates some desync error. We were expecting a @@ -135,95 +141,34 @@ class VisibilityStore { } } - /** - * Update visibility settings for the {@code databaseName}. - * - * @param schemasToRemove Database-prefixed schemas that should be removed - */ - public void updateSchemas(@NonNull String databaseName, @NonNull Set schemasToRemove) - throws AppSearchException { - Preconditions.checkNotNull(databaseName); - Preconditions.checkNotNull(schemasToRemove); - - GenericDocument visibilityDocument; - try { - visibilityDocument = - mAppSearchImpl.getDocument(DATABASE_NAME, NAMESPACE, /*uri=*/ databaseName); - } catch (AppSearchException e) { - if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) { - // This might be the first time we're seeing visibility changes for a database. - // Create a new visibility document. - mAppSearchImpl.putDocument( - DATABASE_NAME, - new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE) - .setNamespace(NAMESPACE) - .build()); - - // Since we know there was nothing that existed before, we don't need to remove - // anything either. Return early. - return; - } - // Otherwise, this is some real error we should pass up. - throw e; - } - - String[] hiddenSchemas = - visibilityDocument.getPropertyStringArray(PLATFORM_HIDDEN_PROPERTY); - if (hiddenSchemas == null) { - // Nothing to remove. - return; - } - - // Create a new set so we can remove from it. - Set remainingSchemas = new ArraySet<>(Arrays.asList(hiddenSchemas)); - boolean changed = remainingSchemas.removeAll(schemasToRemove); - if (!changed) { - // Nothing was actually removed. Can return early. - return; - } - - // Update our persisted document - // TODO(b/171882200): Switch to a .toBuilder API when it's available. - GenericDocument.Builder newVisibilityDocument = - new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE) - .setNamespace(NAMESPACE); - if (!remainingSchemas.isEmpty()) { - newVisibilityDocument.setPropertyString( - PLATFORM_HIDDEN_PROPERTY, remainingSchemas.toArray(new String[0])); - } - mAppSearchImpl.putDocument(DATABASE_NAME, newVisibilityDocument.build()); - - // Update derived data structures - mPlatformHiddenMap.put(databaseName, remainingSchemas); - } - /** * Sets visibility settings for {@code databaseName}. Any previous visibility settings will be * overwritten. * - * @param databaseName Database name that owns the {@code platformHiddenSchemas}. - * @param platformHiddenSchemas Set of database-qualified schemas that should be hidden from the - * platform. + * @param databaseName Database name that owns the {@code schemasNotPlatformSurfaceable}. + * @param schemasNotPlatformSurfaceable Set of database-qualified schemas that should be hidden + * from the platform. * @throws AppSearchException on AppSearchImpl error. */ public void setVisibility( - @NonNull String databaseName, @NonNull Set platformHiddenSchemas) + @NonNull String databaseName, @NonNull Set schemasNotPlatformSurfaceable) throws AppSearchException { Preconditions.checkNotNull(databaseName); - Preconditions.checkNotNull(platformHiddenSchemas); + Preconditions.checkNotNull(schemasNotPlatformSurfaceable); // Persist the document GenericDocument.Builder visibilityDocument = new GenericDocument.Builder(/*uri=*/ databaseName, SCHEMA_TYPE) .setNamespace(NAMESPACE); - if (!platformHiddenSchemas.isEmpty()) { + if (!schemasNotPlatformSurfaceable.isEmpty()) { visibilityDocument.setPropertyString( - PLATFORM_HIDDEN_PROPERTY, platformHiddenSchemas.toArray(new String[0])); + NOT_PLATFORM_SURFACEABLE_PROPERTY, + schemasNotPlatformSurfaceable.toArray(new String[0])); } mAppSearchImpl.putDocument(DATABASE_NAME, visibilityDocument.build()); // Update derived data structures. - mPlatformHiddenMap.put(databaseName, platformHiddenSchemas); + mNotPlatformSurfaceableMap.put(databaseName, schemasNotPlatformSurfaceable); } /** @@ -235,13 +180,13 @@ class VisibilityStore { * none exist. */ @NonNull - public Set getPlatformHiddenSchemas(@NonNull String databaseName) { + public Set getSchemasNotPlatformSurfaceable(@NonNull String databaseName) { Preconditions.checkNotNull(databaseName); - Set platformHiddenSchemas = mPlatformHiddenMap.get(databaseName); - if (platformHiddenSchemas == null) { + Set schemasNotPlatformSurfaceable = mNotPlatformSurfaceableMap.get(databaseName); + if (schemasNotPlatformSurfaceable == null) { return Collections.emptySet(); } - return platformHiddenSchemas; + return schemasNotPlatformSurfaceable; } /** @@ -251,7 +196,7 @@ class VisibilityStore { * @throws AppSearchException on AppSearchImpl error. */ public void handleReset() throws AppSearchException { - mPlatformHiddenMap.clear(); + mNotPlatformSurfaceableMap.clear(); initialize(); } } 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 8f4e7ff69d7c6..5474cd04287c5 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 @@ -39,7 +39,7 @@ public final class GenericDocumentToProtoConverter { /** Converts a {@link GenericDocument} into a {@link DocumentProto}. */ @NonNull @SuppressWarnings("unchecked") - public static DocumentProto convert(@NonNull GenericDocument document) { + public static DocumentProto toDocumentProto(@NonNull GenericDocument document) { Preconditions.checkNotNull(document); DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder(); mProtoBuilder @@ -82,7 +82,7 @@ public final class GenericDocumentToProtoConverter { } } else if (documentValues != null) { for (int j = 0; j < documentValues.length; j++) { - DocumentProto proto = convert(documentValues[j]); + DocumentProto proto = toDocumentProto(documentValues[j]); propertyProto.addDocumentValues(proto); } } else { @@ -96,7 +96,7 @@ public final class GenericDocumentToProtoConverter { /** Converts a {@link DocumentProto} into a {@link GenericDocument}. */ @NonNull - public static GenericDocument convert(@NonNull DocumentProto proto) { + public static GenericDocument toGenericDocument(@NonNull DocumentProto proto) { Preconditions.checkNotNull(proto); GenericDocument.Builder documentBuilder = new GenericDocument.Builder<>(proto.getUri(), proto.getSchema()) @@ -141,7 +141,7 @@ 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] = convert(property.getDocumentValues(j)); + values[j] = toGenericDocument(property.getDocumentValues(j)); } documentBuilder.setPropertyDocument(name, values); } else { 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 642c2a7139304..4165af31c00b9 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 @@ -18,11 +18,13 @@ package com.android.server.appsearch.external.localstorage.converter; 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.PropertyConfigProto; import com.google.android.icing.proto.SchemaTypeConfigProto; +import com.google.android.icing.proto.SchemaTypeConfigProtoOrBuilder; import com.google.android.icing.proto.StringIndexingConfig; import com.google.android.icing.proto.TermMatchType; @@ -34,6 +36,8 @@ import java.util.List; * @hide */ public final class SchemaToProtoConverter { + private static final String TAG = "AppSearchSchemaToProtoC"; + private SchemaToProtoConverter() {} /** @@ -41,23 +45,23 @@ public final class SchemaToProtoConverter { * SchemaTypeConfigProto}. */ @NonNull - public static SchemaTypeConfigProto convert(@NonNull AppSearchSchema schema) { + public static SchemaTypeConfigProto toSchemaTypeConfigProto(@NonNull AppSearchSchema schema) { Preconditions.checkNotNull(schema); SchemaTypeConfigProto.Builder protoBuilder = SchemaTypeConfigProto.newBuilder().setSchemaType(schema.getSchemaType()); List properties = schema.getProperties(); for (int i = 0; i < properties.size(); i++) { - PropertyConfigProto propertyProto = convertProperty(properties.get(i)); + PropertyConfigProto propertyProto = toPropertyConfigProto(properties.get(i)); protoBuilder.addProperties(propertyProto); } return protoBuilder.build(); } @NonNull - private static PropertyConfigProto convertProperty( + private static PropertyConfigProto toPropertyConfigProto( @NonNull AppSearchSchema.PropertyConfig property) { Preconditions.checkNotNull(property); - PropertyConfigProto.Builder propertyConfigProto = + PropertyConfigProto.Builder builder = PropertyConfigProto.newBuilder().setPropertyName(property.getName()); StringIndexingConfig.Builder indexingConfig = StringIndexingConfig.newBuilder(); @@ -68,12 +72,12 @@ public final class SchemaToProtoConverter { if (dataTypeProto == null) { throw new IllegalArgumentException("Invalid dataType: " + dataType); } - propertyConfigProto.setDataType(dataTypeProto); + builder.setDataType(dataTypeProto); // Set schemaType String schemaType = property.getSchemaType(); if (schemaType != null) { - propertyConfigProto.setSchemaType(schemaType); + builder.setSchemaType(schemaType); } // Set cardinality @@ -83,7 +87,7 @@ public final class SchemaToProtoConverter { if (cardinalityProto == null) { throw new IllegalArgumentException("Invalid cardinality: " + dataType); } - propertyConfigProto.setCardinality(cardinalityProto); + builder.setCardinality(cardinalityProto); // Set indexingType @AppSearchSchema.PropertyConfig.IndexingType int indexingType = property.getIndexingType(); @@ -114,7 +118,63 @@ public final class SchemaToProtoConverter { indexingConfig.setTokenizerType(tokenizerTypeProto); // Build! - propertyConfigProto.setStringIndexingConfig(indexingConfig); - return propertyConfigProto.build(); + builder.setStringIndexingConfig(indexingConfig); + return builder.build(); + } + + /** + * Converts a {@link SchemaTypeConfigProto} into an {@link + * android.app.appsearch.AppSearchSchema}. + */ + @NonNull + public static AppSearchSchema toAppSearchSchema(@NonNull SchemaTypeConfigProtoOrBuilder proto) { + Preconditions.checkNotNull(proto); + AppSearchSchema.Builder builder = new AppSearchSchema.Builder(proto.getSchemaType()); + List properties = proto.getPropertiesList(); + for (int i = 0; i < properties.size(); i++) { + AppSearchSchema.PropertyConfig propertyConfig = toPropertyConfig(properties.get(i)); + builder.addProperty(propertyConfig); + } + return builder.build(); + } + + @NonNull + private static AppSearchSchema.PropertyConfig toPropertyConfig( + @NonNull PropertyConfigProto proto) { + Preconditions.checkNotNull(proto); + AppSearchSchema.PropertyConfig.Builder builder = + new AppSearchSchema.PropertyConfig.Builder(proto.getPropertyName()) + .setDataType(proto.getDataType().getNumber()) + .setCardinality(proto.getCardinality().getNumber()) + .setTokenizerType( + proto.getStringIndexingConfig().getTokenizerType().getNumber()); + + // Set schema + if (!proto.getSchemaType().isEmpty()) { + builder.setSchemaType(proto.getSchemaType()); + } + + // Set indexingType + @AppSearchSchema.PropertyConfig.IndexingType int indexingType; + TermMatchType.Code termMatchTypeProto = proto.getStringIndexingConfig().getTermMatchType(); + switch (termMatchTypeProto) { + case UNKNOWN: + indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE; + break; + case EXACT_ONLY: + indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_EXACT_TERMS; + break; + case PREFIX: + indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES; + break; + default: + // Avoid crashing in the 'read' path; we should try to interpret the document to the + // extent possible. + Log.w(TAG, "Invalid indexingType: " + termMatchTypeProto.getNumber()); + indexingType = AppSearchSchema.PropertyConfig.INDEXING_TYPE_NONE; + } + builder.setIndexingType(indexingType); + + return builder.build(); } } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java index b91a393cab4a3..4d107a970abdc 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 @@ -39,13 +39,12 @@ public class SearchResultToProtoConverter { /** Translate a {@link SearchResultProto} into {@link SearchResultPage}. */ @NonNull - public static SearchResultPage convertToSearchResultPage( - @NonNull SearchResultProtoOrBuilder proto) { + public static SearchResultPage toSearchResultPage(@NonNull SearchResultProtoOrBuilder proto) { Bundle bundle = new Bundle(); bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken()); ArrayList resultBundles = new ArrayList<>(proto.getResultsCount()); for (int i = 0; i < proto.getResultsCount(); i++) { - resultBundles.add(convertToSearchResultBundle(proto.getResults(i))); + resultBundles.add(toSearchResultBundle(proto.getResults(i))); } bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles); return new SearchResultPage(bundle); @@ -53,10 +52,11 @@ public class SearchResultToProtoConverter { /** Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. */ @NonNull - private static Bundle convertToSearchResultBundle( + private static Bundle toSearchResultBundle( @NonNull SearchResultProto.ResultProtoOrBuilder proto) { Bundle bundle = new Bundle(); - GenericDocument document = GenericDocumentToProtoConverter.convert(proto.getDocument()); + GenericDocument document = + GenericDocumentToProtoConverter.toGenericDocument(proto.getDocument()); bundle.putBundle(SearchResult.DOCUMENT_FIELD, document.getBundle()); ArrayList matchList = new ArrayList<>(); diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt index a2bf0d504a1f1..57c48d838808a 100644 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ b/apex/appsearch/synced_jetpack_changeid.txt @@ -1 +1 @@ -I2decd83fab4c4d58fe38c9970f804046479c942c +If5fd2bd705d5507d044706701a94b2e1496ef1df diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java new file mode 100644 index 0000000000000..e03cea31f0770 --- /dev/null +++ b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaRequestTest.java @@ -0,0 +1,65 @@ +/* + * 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 SetSchemaRequestTest { + + @Test + public void testInvalidSchemaReferences() { + IllegalArgumentException expected = + expectThrows( + IllegalArgumentException.class, + () -> + new SetSchemaRequest.Builder() + .setSchemaTypeVisibilityForSystemUi(false, "InvalidSchema") + .build()); + assertThat(expected).hasMessageThat().contains("referenced, but were not added"); + } + + @Test + public void testSchemaTypeVisibilityForSystemUi_Visible() { + AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build(); + + // By default, the schema is visible. + SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(schema).build(); + assertThat(request.getSchemasNotPlatformSurfaceable()).isEmpty(); + + request = + new SetSchemaRequest.Builder() + .addSchema(schema) + .setSchemaTypeVisibilityForSystemUi(true, "Schema") + .build(); + assertThat(request.getSchemasNotPlatformSurfaceable()).isEmpty(); + } + + @Test + public void testSchemaTypeVisibilityForSystemUi_NotVisible() { + AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build(); + SetSchemaRequest request = + new SetSchemaRequest.Builder() + .addSchema(schema) + .setSchemaTypeVisibilityForSystemUi(false, "Schema") + .build(); + assertThat(request.getSchemasNotPlatformSurfaceable()).containsExactly("Schema"); + } +} diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java index 2eaebd6e7b938..7072a8161a871 100644 --- a/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/cts/AppSearchSchemaCtsTest.java @@ -78,4 +78,125 @@ public class AppSearchSchemaCtsTest { .build())); assertThat(e).hasMessageThat().contains("Property defined more than once: subject"); } + + @Test + public void testEquals_identical() { + AppSearchSchema schema1 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + AppSearchSchema schema2 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + assertThat(schema1).isEqualTo(schema2); + assertThat(schema1.hashCode()).isEqualTo(schema2.hashCode()); + } + + @Test + public void testEquals_differentOrder() { + AppSearchSchema schema1 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + AppSearchSchema schema2 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .build()) + .build(); + assertThat(schema1).isEqualTo(schema2); + assertThat(schema1.hashCode()).isEqualTo(schema2.hashCode()); + } + + @Test + public void testEquals_failure() { + AppSearchSchema schema1 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + AppSearchSchema schema2 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + PropertyConfig + .INDEXING_TYPE_EXACT_TERMS) // Different + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + assertThat(schema1).isNotEqualTo(schema2); + assertThat(schema1.hashCode()).isNotEqualTo(schema2.hashCode()); + } + + @Test + public void testEquals_failure_differentOrder() { + AppSearchSchema schema1 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new PropertyConfig.Builder("body") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + // Order of 'body' and 'subject' has been switched + AppSearchSchema schema2 = + new AppSearchSchema.Builder("Email") + .addProperty( + new PropertyConfig.Builder("body") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new PropertyConfig.Builder("subject") + .setDataType(PropertyConfig.DATA_TYPE_STRING) + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + assertThat(schema1).isNotEqualTo(schema2); + assertThat(schema1.hashCode()).isNotEqualTo(schema2.hashCode()); + } } diff --git a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java new file mode 100644 index 0000000000000..cfcfcc8cf0443 --- /dev/null +++ b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java @@ -0,0 +1,254 @@ +/* + * 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.util; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Build; +import android.os.Bundle; +import android.os.ParcelUuid; +import android.os.Parcelable; +import android.util.Size; +import android.util.SizeF; +import android.util.SparseArray; + +import com.google.common.collect.ImmutableList; + +import org.junit.Test; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.UUID; + +public class BundleUtilTest { + @Test + public void testDeepEquals_self() { + Bundle one = new Bundle(); + one.putString("a", "a"); + assertThat(BundleUtil.deepEquals(one, one)).isTrue(); + } + + @Test + public void testDeepEquals_simple() { + Bundle one = new Bundle(); + one.putString("a", "a"); + + Bundle two = new Bundle(); + two.putString("a", "a"); + + assertThat(one).isNotEqualTo(two); + assertThat(BundleUtil.deepEquals(one, two)).isTrue(); + } + + @Test + public void testDeepEquals_keyMismatch() { + Bundle one = new Bundle(); + one.putString("a", "a"); + + Bundle two = new Bundle(); + two.putString("a", "a"); + two.putString("b", "b"); + assertThat(BundleUtil.deepEquals(one, two)).isFalse(); + } + + @Test + public void testDeepEquals_thorough_equal() { + Bundle[] inputs = new Bundle[2]; + for (int i = 0; i < 2; i++) { + inputs[i] = createThoroughBundle(); + } + assertThat(inputs[0]).isNotEqualTo(inputs[1]); + assertThat(BundleUtil.deepEquals(inputs[0], inputs[1])).isTrue(); + } + + @Test + public void testDeepEquals_thorough_notEqual() { + Bundle[] inputs = new Bundle[2]; + for (int i = 0; i < 2; i++) { + Bundle b = createThoroughBundle(); + // Create a difference + assertThat(b.containsKey("doubleArray")).isTrue(); + b.putDoubleArray("doubleArray", new double[] {18., i}); + inputs[i] = b; + } + assertThat(inputs[0]).isNotEqualTo(inputs[1]); + assertThat(BundleUtil.deepEquals(inputs[0], inputs[1])).isFalse(); + } + + @Test + public void testDeepEquals_nestedNotEquals() { + Bundle one = new Bundle(); + one.putString("a", "a"); + Bundle two = new Bundle(); + two.putBundle("b", one); + Bundle twoClone = new Bundle(); + twoClone.putBundle("b", one); + Bundle three = new Bundle(); + three.putBundle("b", two); + + ArrayList listOne = new ArrayList<>(ImmutableList.of(one, two, three)); + ArrayList listOneClone = new ArrayList<>(ImmutableList.of(one, twoClone, three)); + ArrayList listTwo = new ArrayList<>(ImmutableList.of(one, three, two)); + Bundle b1 = new Bundle(); + b1.putParcelableArrayList("key", listOne); + Bundle b1Clone = new Bundle(); + b1Clone.putParcelableArrayList("key", listOneClone); + Bundle b2 = new Bundle(); + b2.putParcelableArrayList("key", listTwo); + + assertThat(b1).isNotEqualTo(b1Clone); + assertThat(BundleUtil.deepEquals(b1, b1Clone)).isTrue(); + assertThat(BundleUtil.deepEquals(b1, b2)).isFalse(); + assertThat(BundleUtil.deepEquals(b1Clone, b2)).isFalse(); + } + + @Test + public void testDeepEquals_sparseArray() { + Parcelable parcelable1 = new ParcelUuid(UUID.randomUUID()); + Parcelable parcelable2 = new ParcelUuid(UUID.randomUUID()); + Parcelable parcelable3 = new ParcelUuid(UUID.randomUUID()); + + SparseArray array1 = new SparseArray<>(); + array1.put(1, parcelable1); + array1.put(10, parcelable2); + + SparseArray array1Clone = new SparseArray<>(); + array1Clone.put(1, parcelable1); + array1Clone.put(10, parcelable2); + + SparseArray array2 = new SparseArray<>(); + array2.put(1, parcelable1); + array2.put(10, parcelable3); // Different + + Bundle b1 = new Bundle(); + b1.putSparseParcelableArray("array1", array1); + Bundle b1Clone = new Bundle(); + b1Clone.putSparseParcelableArray("array1", array1Clone); + Bundle b2 = new Bundle(); + b2.putSparseParcelableArray("array1", array2); + + assertThat(b1).isNotEqualTo(b1Clone); + assertThat(BundleUtil.deepEquals(b1, b1Clone)).isTrue(); + assertThat(BundleUtil.deepEquals(b1, b2)).isFalse(); + assertThat(BundleUtil.deepEquals(b1Clone, b2)).isFalse(); + } + + @Test + public void testDeepHashCode_same() { + Bundle[] inputs = new Bundle[2]; + for (int i = 0; i < 2; i++) { + inputs[i] = createThoroughBundle(); + } + assertThat(BundleUtil.deepHashCode(inputs[0])) + .isEqualTo(BundleUtil.deepHashCode(inputs[1])); + } + + @Test + public void testDeepHashCode_different() { + Bundle[] inputs = new Bundle[2]; + for (int i = 0; i < 2; i++) { + Bundle b = createThoroughBundle(); + // Create a difference + assertThat(b.containsKey("doubleArray")).isTrue(); + b.putDoubleArray("doubleArray", new double[] {18., i}); + inputs[i] = b; + } + assertThat(BundleUtil.deepHashCode(inputs[0])) + .isNotEqualTo(BundleUtil.deepHashCode(inputs[1])); + } + + @Test + public void testHashCode_sparseArray() { + Parcelable parcelable1 = new ParcelUuid(UUID.randomUUID()); + Parcelable parcelable2 = new ParcelUuid(UUID.randomUUID()); + Parcelable parcelable3 = new ParcelUuid(UUID.randomUUID()); + + SparseArray array1 = new SparseArray<>(); + array1.put(1, parcelable1); + array1.put(10, parcelable2); + + SparseArray array1Clone = new SparseArray<>(); + array1Clone.put(1, parcelable1); + array1Clone.put(10, parcelable2); + + SparseArray array2 = new SparseArray<>(); + array2.put(1, parcelable1); + array2.put(10, parcelable3); // Different + + Bundle b1 = new Bundle(); + b1.putSparseParcelableArray("array1", array1); + Bundle b1Clone = new Bundle(); + b1Clone.putSparseParcelableArray("array1", array1Clone); + Bundle b2 = new Bundle(); + b2.putSparseParcelableArray("array1", array2); + + assertThat(b1.hashCode()).isNotEqualTo(b1Clone.hashCode()); + assertThat(BundleUtil.deepHashCode(b1)).isEqualTo(BundleUtil.deepHashCode(b1Clone)); + assertThat(BundleUtil.deepHashCode(b1)).isNotEqualTo(BundleUtil.deepHashCode(b2)); + } + + private static Bundle createThoroughBundle() { + Bundle toy1 = new Bundle(); + toy1.putString("a", "a"); + Bundle toy2 = new Bundle(); + toy2.putInt("b", 2); + + Bundle b = new Bundle(); + // BaseBundle stuff + b.putBoolean("boolean", true); + b.putByte("byte", (byte) 1); + b.putChar("char", 'a'); + b.putShort("short", (short) 2); + b.putInt("int", 3); + b.putLong("long", 4L); + b.putFloat("float", 5f); + b.putDouble("double", 6f); + b.putString("string", "b"); + b.putCharSequence("charSequence", "c"); + b.putIntegerArrayList("integerArrayList", new ArrayList<>(ImmutableList.of(7, 8))); + b.putStringArrayList("stringArrayList", new ArrayList<>(ImmutableList.of("d", "e"))); + b.putCharSequenceArrayList( + "charSequenceArrayList", new ArrayList<>(ImmutableList.of("f", "g"))); + b.putSerializable("serializable", new BigDecimal(9)); + b.putBooleanArray("booleanArray", new boolean[] {true, false, true}); + b.putByteArray("byteArray", new byte[] {(byte) 10, (byte) 11}); + b.putShortArray("shortArray", new short[] {(short) 12, (short) 13}); + b.putCharArray("charArray", new char[] {'h', 'i'}); + b.putLongArray("longArray", new long[] {14L, 15L}); + b.putFloatArray("floatArray", new float[] {16f, 17f}); + b.putDoubleArray("doubleArray", new double[] {18., 19.}); + b.putStringArray("stringArray", new String[] {"j", "k"}); + b.putCharSequenceArray("charSequenceArrayList", new CharSequence[] {"l", "m"}); + + // Bundle stuff + b.putParcelable("parcelable", toy1); + if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { + b.putSize("size", new Size(20, 21)); + b.putSizeF("sizeF", new SizeF(22f, 23f)); + } + b.putParcelableArray("parcelableArray", new Parcelable[] {toy1, toy2}); + b.putParcelableArrayList( + "parcelableArrayList", new ArrayList<>(ImmutableList.of(toy1, toy2))); + SparseArray sparseArray = new SparseArray<>(); + sparseArray.put(24, toy1); + sparseArray.put(1025, toy2); + b.putSparseParcelableArray("sparceParcelableArray", sparseArray); + b.putBundle("bundle", toy1); + + return b; + } +} 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 b929061a967e7..f38def8c3c777 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 @@ -37,6 +37,7 @@ import com.android.server.appsearch.proto.SearchSpecProto; 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.ImmutableSet; import org.junit.Before; @@ -46,9 +47,7 @@ import org.junit.rules.TemporaryFolder; import java.util.ArrayList; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; public class AppSearchImplTest { @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); @@ -66,14 +65,15 @@ public class AppSearchImplTest { + VisibilityStore.SCHEMA_TYPE) .addProperty( new AppSearchSchema.PropertyConfig.Builder( - VisibilityStore.PLATFORM_HIDDEN_PROPERTY) + VisibilityStore.NOT_PLATFORM_SURFACEABLE_PROPERTY) .setDataType( AppSearchSchema.PropertyConfig.DATA_TYPE_STRING) .setCardinality( AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) .build()) .build(); - mVisibilitySchemaProto = SchemaToProtoConverter.convert(visibilityAppSearchSchema); + mVisibilitySchemaProto = + SchemaToProtoConverter.toSchemaTypeConfigProto(visibilityAppSearchSchema); } /** @@ -340,9 +340,13 @@ public class AppSearchImplTest { @Test public void testOptimize() throws Exception { // Insert schema - Set schemas = - Collections.singleton(new AppSearchSchema.Builder("type").build()); - mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/ false); + List schemas = + Collections.singletonList(new AppSearchSchema.Builder("type").build()); + mAppSearchImpl.setSchema( + "database", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); // Insert enough documents. for (int i = 0; @@ -351,7 +355,7 @@ public class AppSearchImplTest { + AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) { GenericDocument document = - new GenericDocument.Builder("uri" + i, "type") + new GenericDocument.Builder<>("uri" + i, "type") .setNamespace("namespace") .build(); mAppSearchImpl.putDocument("database", document); @@ -392,13 +396,17 @@ public class AppSearchImplTest { SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery(""); // Insert schema - Set schemas = - Collections.singleton(new AppSearchSchema.Builder("type").build()); - mAppSearchImpl.setSchema("database", schemas, /*forceOverride=*/ false); + List schemas = + Collections.singletonList(new AppSearchSchema.Builder("type").build()); + mAppSearchImpl.setSchema( + "database", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); // Insert document GenericDocument document = - new GenericDocument.Builder("uri", "type").setNamespace("namespace").build(); + new GenericDocument.Builder<>("uri", "type").setNamespace("namespace").build(); mAppSearchImpl.putDocument("database", document); // Rewrite SearchSpec @@ -413,20 +421,28 @@ public class AppSearchImplTest { SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery(""); // Insert schema - Set schemas = - Set.of( + List schemas = + ImmutableList.of( new AppSearchSchema.Builder("typeA").build(), new AppSearchSchema.Builder("typeB").build()); - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false); - mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/ false); + mAppSearchImpl.setSchema( + "database1", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); + mAppSearchImpl.setSchema( + "database2", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); // Insert documents GenericDocument document1 = - new GenericDocument.Builder("uri", "typeA").setNamespace("namespace").build(); + new GenericDocument.Builder<>("uri", "typeA").setNamespace("namespace").build(); mAppSearchImpl.putDocument("database1", document1); GenericDocument document2 = - new GenericDocument.Builder("uri", "typeB").setNamespace("namespace").build(); + new GenericDocument.Builder<>("uri", "typeB").setNamespace("namespace").build(); mAppSearchImpl.putDocument("database2", document2); // Rewrite SearchSpec @@ -477,10 +493,14 @@ public class AppSearchImplTest { @Test public void testSetSchema() throws Exception { - Set schemas = - Collections.singleton(new AppSearchSchema.Builder("Email").build()); + List schemas = + Collections.singletonList(new AppSearchSchema.Builder("Email").build()); // Set schema Email to AppSearch database1 - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false); + mAppSearchImpl.setSchema( + "database1", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); // Create expected schemaType proto. SchemaProto expectedProto = @@ -500,35 +520,47 @@ public class AppSearchImplTest { public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception { mAppSearchImpl.setSchema( "database", - Collections.singleton(new AppSearchSchema.Builder("schema1").build()), + Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"), /*forceOverride=*/ false); - mAppSearchImpl.setVisibility("database", Set.of("schema1")); // "schema1" is platform hidden now - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .containsExactly("database/schema1"); // Add a new schema, and include the already-existing "schema1" mAppSearchImpl.setSchema( "database", - Set.of( + ImmutableList.of( new AppSearchSchema.Builder("schema1").build(), new AppSearchSchema.Builder("schema2").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"), /*forceOverride=*/ false); // Check that "schema1" is still platform hidden, but "schema2" is the default platform // visible. - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .containsExactly("database/schema1"); } @Test public void testRemoveSchema() throws Exception { - Set schemas = new HashSet<>(); - schemas.add(new AppSearchSchema.Builder("Email").build()); - schemas.add(new AppSearchSchema.Builder("Document").build()); + List schemas = + ImmutableList.of( + new AppSearchSchema.Builder("Email").build(), + new AppSearchSchema.Builder("Document").build()); // Set schema Email and Document to AppSearch database1 - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false); + mAppSearchImpl.setSchema( + "database1", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); // Create expected schemaType proto. SchemaProto expectedProto = @@ -547,20 +579,27 @@ public class AppSearchImplTest { assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) .containsExactlyElementsIn(expectedTypes); - final Set finalSchemas = - Collections.singleton(new AppSearchSchema.Builder("Email").build()); + final List finalSchemas = + Collections.singletonList(new AppSearchSchema.Builder("Email").build()); // Check the incompatible error has been thrown. AppSearchException e = expectThrows( AppSearchException.class, () -> mAppSearchImpl.setSchema( - "database1", finalSchemas, /*forceOverride=*/ false)); + "database1", + finalSchemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false)); assertThat(e).hasMessageThat().contains("Schema is incompatible"); assertThat(e).hasMessageThat().contains("Deleted types: [database1/Document]"); // ForceOverride to delete. - mAppSearchImpl.setSchema("database1", finalSchemas, /*forceOverride=*/ true); + mAppSearchImpl.setSchema( + "database1", + finalSchemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ true); // Check Document schema is removed. expectedProto = @@ -579,13 +618,22 @@ public class AppSearchImplTest { @Test public void testRemoveSchema_differentDataBase() throws Exception { // Create schemas - Set schemas = new HashSet<>(); - schemas.add(new AppSearchSchema.Builder("Email").build()); - schemas.add(new AppSearchSchema.Builder("Document").build()); + List schemas = + ImmutableList.of( + new AppSearchSchema.Builder("Email").build(), + new AppSearchSchema.Builder("Document").build()); // Set schema Email and Document to AppSearch database1 and 2 - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ false); - mAppSearchImpl.setSchema("database2", schemas, /*forceOverride=*/ false); + mAppSearchImpl.setSchema( + "database1", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); + mAppSearchImpl.setSchema( + "database2", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ false); // Create expected schemaType proto. SchemaProto expectedProto = @@ -610,8 +658,12 @@ public class AppSearchImplTest { .containsExactlyElementsIn(expectedTypes); // Save only Email to database1 this time. - schemas = Collections.singleton(new AppSearchSchema.Builder("Email").build()); - mAppSearchImpl.setSchema("database1", schemas, /*forceOverride=*/ true); + schemas = Collections.singletonList(new AppSearchSchema.Builder("Email").build()); + mAppSearchImpl.setSchema( + "database1", + schemas, + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ true); // Create expected schemaType list, database 1 should only contain Email but database 2 // remains in same. @@ -638,68 +690,73 @@ public class AppSearchImplTest { public void testRemoveSchema_removedFromVisibilityStore() throws Exception { mAppSearchImpl.setSchema( "database", - Collections.singleton(new AppSearchSchema.Builder("schema1").build()), + Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"), /*forceOverride=*/ false); - mAppSearchImpl.setVisibility("database", Set.of("schema1")); // "schema1" is platform hidden now - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .containsExactly("database/schema1"); // Remove "schema1" by force overriding - mAppSearchImpl.setSchema("database", Collections.emptySet(), /*forceOverride=*/ true); + mAppSearchImpl.setSchema( + "database", + Collections.emptyList(), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), + /*forceOverride=*/ true); // Check that "schema1" is no longer considered platform hidden - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .isEmpty(); // Add "schema1" back, it gets default visibility settings which means it's not platform // hidden. mAppSearchImpl.setSchema( "database", - Collections.singleton(new AppSearchSchema.Builder("schema1").build()), + Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*forceOverride=*/ false); - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .isEmpty(); } @Test - public void testSetVisibility_defaultPlatformVisible() throws Exception { + public void testSetSchema_defaultPlatformVisible() throws Exception { mAppSearchImpl.setSchema( "database", - Collections.singleton(new AppSearchSchema.Builder("Schema").build()), + Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*forceOverride=*/ false); - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .isEmpty(); } @Test - public void testSetVisibility_platformHidden() throws Exception { + public void testSetSchema_platformHidden() throws Exception { mAppSearchImpl.setSchema( "database", - Collections.singleton(new AppSearchSchema.Builder("Schema").build()), + Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"), /*forceOverride=*/ false); - mAppSearchImpl.setVisibility("database", Set.of("Schema")); - assertThat(mAppSearchImpl.getVisibilityStoreLocked().getPlatformHiddenSchemas("database")) + assertThat( + mAppSearchImpl + .getVisibilityStoreLocked() + .getSchemasNotPlatformSurfaceable("database")) .containsExactly("database/Schema"); } - @Test - public void testSetVisibility_unknownSchema() throws Exception { - mAppSearchImpl.setSchema( - "database", - Collections.singleton(new AppSearchSchema.Builder("Schema").build()), - /*forceOverride=*/ false); - - // We'll throw an exception if a client tries to set visibility on a schema we don't know - // about. - AppSearchException e = - expectThrows( - AppSearchException.class, - () -> mAppSearchImpl.setVisibility("database", Set.of("UnknownSchema"))); - assertThat(e).hasMessageThat().contains("Unknown schema(s)"); - } - @Test public void testHasSchemaType() throws Exception { // Nothing exists yet @@ -707,7 +764,8 @@ public class AppSearchImplTest { mAppSearchImpl.setSchema( "database", - Collections.singleton(new AppSearchSchema.Builder("Schema").build()), + Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*forceOverride=*/ false); assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isTrue(); @@ -723,7 +781,8 @@ public class AppSearchImplTest { // Has database1 mAppSearchImpl.setSchema( "database1", - Collections.singleton(new AppSearchSchema.Builder("schema").build()), + Collections.singletonList(new AppSearchSchema.Builder("schema").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*forceOverride=*/ false); assertThat(mAppSearchImpl.getDatabasesLocked()) .containsExactly(VisibilityStore.DATABASE_NAME, "database1"); @@ -731,7 +790,8 @@ public class AppSearchImplTest { // Has both databases mAppSearchImpl.setSchema( "database2", - Collections.singleton(new AppSearchSchema.Builder("schema").build()), + Collections.singletonList(new AppSearchSchema.Builder("schema").build()), + /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*forceOverride=*/ false); assertThat(mAppSearchImpl.getDatabasesLocked()) .containsExactly(VisibilityStore.DATABASE_NAME, "database1", "database2"); diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java index dfe2de6538a49..a1f575a8720ee 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/VisibilityStoreTest.java @@ -41,34 +41,19 @@ public class VisibilityStoreTest { @Test public void testSetVisibility() throws Exception { mVisibilityStore.setVisibility( - "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2")); - assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")) + "database", /*schemasNotPlatformSurfaceable=*/ Set.of("schema1", "schema2")); + assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database")) .containsExactly("schema1", "schema2"); // New .setVisibility() call completely overrides previous visibility settings. So // "schema1" isn't preserved. mVisibilityStore.setVisibility( - "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema3")); - assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")) + "database", /*schemasNotPlatformSurfaceable=*/ Set.of("schema1", "schema3")); + assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database")) .containsExactly("schema1", "schema3"); mVisibilityStore.setVisibility( - "database", /*platformHiddenSchemas=*/ Collections.emptySet()); - assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty(); - } - - @Test - public void testRemoveSchemas() throws Exception { - mVisibilityStore.setVisibility( - "database", /*platformHiddenSchemas=*/ Set.of("schema1", "schema2")); - - // Removed just schema1 - mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema1")); - assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")) - .containsExactly("schema2"); - - // Removed everything now - mVisibilityStore.updateSchemas("database", /*schemasToRemove=*/ Set.of("schema2")); - assertThat(mVisibilityStore.getPlatformHiddenSchemas("database")).isEmpty(); + "database", /*schemasNotPlatformSurfaceable=*/ Collections.emptySet()); + assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database")).isEmpty(); } } 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 98392a71be589..194be3761903b 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 @@ -94,20 +94,24 @@ public class GenericDocumentToProtoConverterTest { PropertyProto.newBuilder() .setName("documentKey1") .addDocumentValues( - GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_1))); + GenericDocumentToProtoConverter.toDocumentProto( + DOCUMENT_PROPERTIES_1))); propertyProtoMap.put( "documentKey2", PropertyProto.newBuilder() .setName("documentKey2") .addDocumentValues( - GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_2))); + GenericDocumentToProtoConverter.toDocumentProto( + DOCUMENT_PROPERTIES_2))); List sortedKey = new ArrayList<>(propertyProtoMap.keySet()); Collections.sort(sortedKey); for (String key : sortedKey) { documentProtoBuilder.addProperties(propertyProtoMap.get(key)); } DocumentProto documentProto = documentProtoBuilder.build(); - assertThat(GenericDocumentToProtoConverter.convert(document)).isEqualTo(documentProto); - assertThat(document).isEqualTo(GenericDocumentToProtoConverter.convert(documentProto)); + assertThat(GenericDocumentToProtoConverter.toDocumentProto(document)) + .isEqualTo(documentProto); + assertThat(document) + .isEqualTo(GenericDocumentToProtoConverter.toGenericDocument(documentProto)); } } diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java index dedfca42ff90f..88edcb857aaf3 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java @@ -89,7 +89,10 @@ public class SchemaToProtoConverterTest { TermMatchType.Code.PREFIX))) .build(); - assertThat(SchemaToProtoConverter.convert(emailSchema)).isEqualTo(expectedEmailProto); + assertThat(SchemaToProtoConverter.toSchemaTypeConfigProto(emailSchema)) + .isEqualTo(expectedEmailProto); + assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedEmailProto)) + .isEqualTo(emailSchema); } @Test @@ -151,7 +154,9 @@ public class SchemaToProtoConverterTest { TermMatchType.Code.UNKNOWN))) .build(); - assertThat(SchemaToProtoConverter.convert(musicRecordingSchema)) + assertThat(SchemaToProtoConverter.toSchemaTypeConfigProto(musicRecordingSchema)) .isEqualTo(expectedMusicRecordingProto); + assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedMusicRecordingProto)) + .isEqualTo(musicRecordingSchema); } } 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 518f53205588a..7c68c6be4883b 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 @@ -83,7 +83,7 @@ public class SnippetTest { // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = - SearchResultToProtoConverter.convertToSearchResultPage(searchResultProto); + SearchResultToProtoConverter.toSearchResultPage(searchResultProto); for (SearchResult result : searchResultPage.getResults()) { SearchResult.MatchInfo match = result.getMatches().get(0); assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString); @@ -131,7 +131,7 @@ public class SnippetTest { SearchResultProto.newBuilder().addResults(resultProto).build(); SearchResultPage searchResultPage = - SearchResultToProtoConverter.convertToSearchResultPage(searchResultProto); + SearchResultToProtoConverter.toSearchResultPage(searchResultProto); for (SearchResult result : searchResultPage.getResults()) { assertThat(result.getMatches()).isEmpty(); } @@ -196,7 +196,7 @@ public class SnippetTest { // Making ResultReader and getting Snippet values. SearchResultPage searchResultPage = - SearchResultToProtoConverter.convertToSearchResultPage(searchResultProto); + SearchResultToProtoConverter.toSearchResultPage(searchResultProto); for (SearchResult result : searchResultPage.getResults()) { SearchResult.MatchInfo match1 = result.getMatches().get(0);