Merge "Update framework from jetpack." into sc-dev

This commit is contained in:
Alexander Dorokhine
2021-04-15 05:52:11 +00:00
committed by Android (Google) Code Review
39 changed files with 1158 additions and 517 deletions

View File

@@ -37,6 +37,8 @@ import java.util.Objects;
* @param <ValueType> The type of result object for successful calls. * @param <ValueType> The type of result object for successful calls.
*/ */
public final class AppSearchResult<ValueType> implements Parcelable { public final class AppSearchResult<ValueType> implements Parcelable {
private static final String TAG = "AppSearchResult";
/** /**
* Result codes from {@link AppSearchSession} methods. * Result codes from {@link AppSearchSession} methods.
* @hide * @hide
@@ -246,14 +248,22 @@ public final class AppSearchResult<ValueType> implements Parcelable {
@NonNull @NonNull
public static <ValueType> AppSearchResult<ValueType> throwableToFailedResult( public static <ValueType> AppSearchResult<ValueType> throwableToFailedResult(
@NonNull Throwable t) { @NonNull Throwable t) {
Log.d("AppSearchResult", "Converting throwable to failed result.", t); // Log for traceability. NOT_FOUND is logged at VERBOSE because this error can occur during
// the regular operation of the system (b/183550974). Everything else is logged at DEBUG.
if (t instanceof AppSearchException
&& ((AppSearchException) t).getResultCode() == RESULT_NOT_FOUND) {
Log.v(TAG, "Converting throwable to failed result: " + t);
} else {
Log.d(TAG, "Converting throwable to failed result.", t);
}
if (t instanceof AppSearchException) { if (t instanceof AppSearchException) {
return ((AppSearchException) t).toAppSearchResult(); return ((AppSearchException) t).toAppSearchResult();
} }
String exceptionClass = t.getClass().getSimpleName();
@AppSearchResult.ResultCode int resultCode; @AppSearchResult.ResultCode int resultCode;
if (t instanceof IllegalStateException) { if (t instanceof IllegalStateException || t instanceof NullPointerException) {
resultCode = AppSearchResult.RESULT_INTERNAL_ERROR; resultCode = AppSearchResult.RESULT_INTERNAL_ERROR;
} else if (t instanceof IllegalArgumentException) { } else if (t instanceof IllegalArgumentException) {
resultCode = AppSearchResult.RESULT_INVALID_ARGUMENT; resultCode = AppSearchResult.RESULT_INVALID_ARGUMENT;
@@ -262,6 +272,6 @@ public final class AppSearchResult<ValueType> implements Parcelable {
} else { } else {
resultCode = AppSearchResult.RESULT_UNKNOWN_ERROR; resultCode = AppSearchResult.RESULT_UNKNOWN_ERROR;
} }
return AppSearchResult.newFailedResult(resultCode, t.getMessage()); return AppSearchResult.newFailedResult(resultCode, exceptionClass + ": " + t.getMessage());
} }
} }

View File

@@ -51,7 +51,7 @@ public final class AppSearchSchema {
/** @hide */ /** @hide */
public AppSearchSchema(@NonNull Bundle bundle) { public AppSearchSchema(@NonNull Bundle bundle) {
Preconditions.checkNotNull(bundle); Objects.requireNonNull(bundle);
mBundle = bundle; mBundle = bundle;
} }
@@ -125,7 +125,7 @@ public final class AppSearchSchema {
/** Creates a new {@link AppSearchSchema.Builder}. */ /** Creates a new {@link AppSearchSchema.Builder}. */
public Builder(@NonNull String schemaType) { public Builder(@NonNull String schemaType) {
Preconditions.checkNotNull(schemaType); Objects.requireNonNull(schemaType);
mSchemaType = schemaType; mSchemaType = schemaType;
} }
@@ -133,7 +133,7 @@ public final class AppSearchSchema {
@NonNull @NonNull
public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) { public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(propertyConfig); Objects.requireNonNull(propertyConfig);
String name = propertyConfig.getName(); String name = propertyConfig.getName();
if (!mPropertyNames.add(name)) { if (!mPropertyNames.add(name)) {
throw new IllegalSchemaException("Property defined more than once: " + name); throw new IllegalSchemaException("Property defined more than once: " + name);
@@ -246,7 +246,7 @@ public final class AppSearchSchema {
@Nullable private Integer mHashCode; @Nullable private Integer mHashCode;
PropertyConfig(@NonNull Bundle bundle) { PropertyConfig(@NonNull Bundle bundle) {
mBundle = Preconditions.checkNotNull(bundle); mBundle = Objects.requireNonNull(bundle);
} }
@Override @Override
@@ -712,7 +712,7 @@ public final class AppSearchSchema {
/** Returns the logical schema-type of the contents of this document property. */ /** Returns the logical schema-type of the contents of this document property. */
@NonNull @NonNull
public String getSchemaType() { public String getSchemaType() {
return Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD)); return Objects.requireNonNull(mBundle.getString(SCHEMA_TYPE_FIELD));
} }
/** /**
@@ -755,7 +755,7 @@ public final class AppSearchSchema {
@NonNull @NonNull
public DocumentPropertyConfig.Builder setSchemaType(@NonNull String schemaType) { public DocumentPropertyConfig.Builder setSchemaType(@NonNull String schemaType) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(schemaType); Objects.requireNonNull(schemaType);
mBundle.putString(SCHEMA_TYPE_FIELD, schemaType); mBundle.putString(SCHEMA_TYPE_FIELD, schemaType);
return this; return this;
} }

View File

@@ -31,6 +31,7 @@ import java.lang.reflect.Array;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Objects;
import java.util.Set; import java.util.Set;
/** /**
@@ -101,11 +102,11 @@ public class GenericDocument {
* @hide * @hide
*/ */
public GenericDocument(@NonNull Bundle bundle) { public GenericDocument(@NonNull Bundle bundle) {
Preconditions.checkNotNull(bundle); Objects.requireNonNull(bundle);
mBundle = bundle; mBundle = bundle;
mProperties = Preconditions.checkNotNull(bundle.getParcelable(PROPERTIES_FIELD)); mProperties = Objects.requireNonNull(bundle.getParcelable(PROPERTIES_FIELD));
mUri = Preconditions.checkNotNull(mBundle.getString(URI_FIELD)); mUri = Objects.requireNonNull(mBundle.getString(URI_FIELD));
mSchemaType = Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD)); mSchemaType = Objects.requireNonNull(mBundle.getString(SCHEMA_TYPE_FIELD));
mCreationTimestampMillis = mCreationTimestampMillis =
mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis()); mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis());
} }
@@ -199,7 +200,7 @@ public class GenericDocument {
*/ */
@Nullable @Nullable
public Object getProperty(@NonNull String key) { public Object getProperty(@NonNull String key) {
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
Object property = mProperties.get(key); Object property = mProperties.get(key);
if (property instanceof ArrayList) { if (property instanceof ArrayList) {
return getPropertyBytesArray(key); return getPropertyBytesArray(key);
@@ -218,7 +219,7 @@ public class GenericDocument {
*/ */
@Nullable @Nullable
public String getPropertyString(@NonNull String key) { public String getPropertyString(@NonNull String key) {
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
String[] propertyArray = getPropertyStringArray(key); String[] propertyArray = getPropertyStringArray(key);
if (propertyArray == null || propertyArray.length == 0) { if (propertyArray == null || propertyArray.length == 0) {
return null; return null;
@@ -235,7 +236,7 @@ public class GenericDocument {
* there is no such key or the value is of a different type. * there is no such key or the value is of a different type.
*/ */
public long getPropertyLong(@NonNull String key) { public long getPropertyLong(@NonNull String key) {
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
long[] propertyArray = getPropertyLongArray(key); long[] propertyArray = getPropertyLongArray(key);
if (propertyArray == null || propertyArray.length == 0) { if (propertyArray == null || propertyArray.length == 0) {
return 0; return 0;
@@ -252,7 +253,7 @@ public class GenericDocument {
* if there is no such key or the value is of a different type. * if there is no such key or the value is of a different type.
*/ */
public double getPropertyDouble(@NonNull String key) { public double getPropertyDouble(@NonNull String key) {
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
double[] propertyArray = getPropertyDoubleArray(key); double[] propertyArray = getPropertyDoubleArray(key);
if (propertyArray == null || propertyArray.length == 0) { if (propertyArray == null || propertyArray.length == 0) {
return 0.0; return 0.0;
@@ -269,7 +270,7 @@ public class GenericDocument {
* false} if there is no such key or the value is of a different type. * false} if there is no such key or the value is of a different type.
*/ */
public boolean getPropertyBoolean(@NonNull String key) { public boolean getPropertyBoolean(@NonNull String key) {
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
boolean[] propertyArray = getPropertyBooleanArray(key); boolean[] propertyArray = getPropertyBooleanArray(key);
if (propertyArray == null || propertyArray.length == 0) { if (propertyArray == null || propertyArray.length == 0) {
return false; return false;
@@ -287,7 +288,7 @@ public class GenericDocument {
*/ */
@Nullable @Nullable
public byte[] getPropertyBytes(@NonNull String key) { public byte[] getPropertyBytes(@NonNull String key) {
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
byte[][] propertyArray = getPropertyBytesArray(key); byte[][] propertyArray = getPropertyBytesArray(key);
if (propertyArray == null || propertyArray.length == 0) { if (propertyArray == null || propertyArray.length == 0) {
return null; return null;
@@ -305,7 +306,7 @@ public class GenericDocument {
*/ */
@Nullable @Nullable
public GenericDocument getPropertyDocument(@NonNull String key) { public GenericDocument getPropertyDocument(@NonNull String key) {
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
GenericDocument[] propertyArray = getPropertyDocumentArray(key); GenericDocument[] propertyArray = getPropertyDocumentArray(key);
if (propertyArray == null || propertyArray.length == 0) { if (propertyArray == null || propertyArray.length == 0) {
return null; return null;
@@ -342,7 +343,7 @@ public class GenericDocument {
*/ */
@Nullable @Nullable
public String[] getPropertyStringArray(@NonNull String key) { public String[] getPropertyStringArray(@NonNull String key) {
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
return getAndCastPropertyArray(key, String[].class); return getAndCastPropertyArray(key, String[].class);
} }
@@ -355,7 +356,7 @@ public class GenericDocument {
*/ */
@Nullable @Nullable
public long[] getPropertyLongArray(@NonNull String key) { public long[] getPropertyLongArray(@NonNull String key) {
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
return getAndCastPropertyArray(key, long[].class); return getAndCastPropertyArray(key, long[].class);
} }
@@ -368,7 +369,7 @@ public class GenericDocument {
*/ */
@Nullable @Nullable
public double[] getPropertyDoubleArray(@NonNull String key) { public double[] getPropertyDoubleArray(@NonNull String key) {
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
return getAndCastPropertyArray(key, double[].class); return getAndCastPropertyArray(key, double[].class);
} }
@@ -381,7 +382,7 @@ public class GenericDocument {
*/ */
@Nullable @Nullable
public boolean[] getPropertyBooleanArray(@NonNull String key) { public boolean[] getPropertyBooleanArray(@NonNull String key) {
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
return getAndCastPropertyArray(key, boolean[].class); return getAndCastPropertyArray(key, boolean[].class);
} }
@@ -396,7 +397,7 @@ public class GenericDocument {
@Nullable @Nullable
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public byte[][] getPropertyBytesArray(@NonNull String key) { public byte[][] getPropertyBytesArray(@NonNull String key) {
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
ArrayList<Bundle> bundles = getAndCastPropertyArray(key, ArrayList.class); ArrayList<Bundle> bundles = getAndCastPropertyArray(key, ArrayList.class);
if (bundles == null || bundles.size() == 0) { if (bundles == null || bundles.size() == 0) {
return null; return null;
@@ -428,7 +429,7 @@ public class GenericDocument {
@SuppressLint("ArrayReturn") @SuppressLint("ArrayReturn")
@Nullable @Nullable
public GenericDocument[] getPropertyDocumentArray(@NonNull String key) { public GenericDocument[] getPropertyDocumentArray(@NonNull String key) {
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
Parcelable[] bundles = getAndCastPropertyArray(key, Parcelable[].class); Parcelable[] bundles = getAndCastPropertyArray(key, Parcelable[].class);
if (bundles == null || bundles.length == 0) { if (bundles == null || bundles.length == 0) {
return null; return null;
@@ -591,9 +592,9 @@ public class GenericDocument {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public Builder(@NonNull String namespace, @NonNull String uri, @NonNull String schemaType) { public Builder(@NonNull String namespace, @NonNull String uri, @NonNull String schemaType) {
Preconditions.checkNotNull(namespace); Objects.requireNonNull(namespace);
Preconditions.checkNotNull(uri); Objects.requireNonNull(uri);
Preconditions.checkNotNull(schemaType); Objects.requireNonNull(schemaType);
mBuilderTypeInstance = (BuilderType) this; mBuilderTypeInstance = (BuilderType) this;
mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace); mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace);
mBundle.putString(GenericDocument.URI_FIELD, uri); mBundle.putString(GenericDocument.URI_FIELD, uri);
@@ -682,8 +683,8 @@ public class GenericDocument {
@NonNull @NonNull
public BuilderType setPropertyString(@NonNull String key, @NonNull String... values) { public BuilderType setPropertyString(@NonNull String key, @NonNull String... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
Preconditions.checkNotNull(values); Objects.requireNonNull(values);
putInPropertyBundle(key, values); putInPropertyBundle(key, values);
return mBuilderTypeInstance; return mBuilderTypeInstance;
} }
@@ -694,15 +695,14 @@ public class GenericDocument {
* *
* @param key the key associated with the {@code values}. * @param key the key associated with the {@code values}.
* @param values the {@code boolean} values of the property. * @param values the {@code boolean} values of the property.
* @throws IllegalArgumentException if no values are provided or if values exceed maximum * @throws IllegalArgumentException if values exceed maximum repeated property length.
* repeated property length.
* @throws IllegalStateException if the builder has already been used. * @throws IllegalStateException if the builder has already been used.
*/ */
@NonNull @NonNull
public BuilderType setPropertyBoolean(@NonNull String key, @NonNull boolean... values) { public BuilderType setPropertyBoolean(@NonNull String key, @NonNull boolean... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
Preconditions.checkNotNull(values); Objects.requireNonNull(values);
putInPropertyBundle(key, values); putInPropertyBundle(key, values);
return mBuilderTypeInstance; return mBuilderTypeInstance;
} }
@@ -712,15 +712,14 @@ public class GenericDocument {
* *
* @param key the key associated with the {@code values}. * @param key the key associated with the {@code values}.
* @param values the {@code long} values of the property. * @param values the {@code long} values of the property.
* @throws IllegalArgumentException if no values are provided or if values exceed maximum * @throws IllegalArgumentException if values exceed maximum repeated property length.
* repeated property length.
* @throws IllegalStateException if the builder has already been used. * @throws IllegalStateException if the builder has already been used.
*/ */
@NonNull @NonNull
public BuilderType setPropertyLong(@NonNull String key, @NonNull long... values) { public BuilderType setPropertyLong(@NonNull String key, @NonNull long... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
Preconditions.checkNotNull(values); Objects.requireNonNull(values);
putInPropertyBundle(key, values); putInPropertyBundle(key, values);
return mBuilderTypeInstance; return mBuilderTypeInstance;
} }
@@ -730,15 +729,14 @@ public class GenericDocument {
* *
* @param key the key associated with the {@code values}. * @param key the key associated with the {@code values}.
* @param values the {@code double} values of the property. * @param values the {@code double} values of the property.
* @throws IllegalArgumentException if no values are provided or if values exceed maximum * @throws IllegalArgumentException if values exceed maximum repeated property length.
* repeated property length.
* @throws IllegalStateException if the builder has already been used. * @throws IllegalStateException if the builder has already been used.
*/ */
@NonNull @NonNull
public BuilderType setPropertyDouble(@NonNull String key, @NonNull double... values) { public BuilderType setPropertyDouble(@NonNull String key, @NonNull double... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
Preconditions.checkNotNull(values); Objects.requireNonNull(values);
putInPropertyBundle(key, values); putInPropertyBundle(key, values);
return mBuilderTypeInstance; return mBuilderTypeInstance;
} }
@@ -755,8 +753,8 @@ public class GenericDocument {
@NonNull @NonNull
public BuilderType setPropertyBytes(@NonNull String key, @NonNull byte[]... values) { public BuilderType setPropertyBytes(@NonNull String key, @NonNull byte[]... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
Preconditions.checkNotNull(values); Objects.requireNonNull(values);
putInPropertyBundle(key, values); putInPropertyBundle(key, values);
return mBuilderTypeInstance; return mBuilderTypeInstance;
} }
@@ -776,8 +774,8 @@ public class GenericDocument {
public BuilderType setPropertyDocument( public BuilderType setPropertyDocument(
@NonNull String key, @NonNull GenericDocument... values) { @NonNull String key, @NonNull GenericDocument... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(key); Objects.requireNonNull(key);
Preconditions.checkNotNull(values); Objects.requireNonNull(values);
putInPropertyBundle(key, values); putInPropertyBundle(key, values);
return mBuilderTypeInstance; return mBuilderTypeInstance;
} }
@@ -850,9 +848,7 @@ public class GenericDocument {
} }
private static void validateRepeatedPropertyLength(@NonNull String key, int length) { private static void validateRepeatedPropertyLength(@NonNull String key, int length) {
if (length == 0) { if (length > MAX_REPEATED_PROPERTY_LENGTH) {
throw new IllegalArgumentException("The input array is empty.");
} else if (length > MAX_REPEATED_PROPERTY_LENGTH) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Repeated property \"" "Repeated property \""
+ key + key

View File

@@ -28,6 +28,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
/** /**
@@ -52,9 +53,9 @@ public final class GetByUriRequest {
@NonNull String namespace, @NonNull String namespace,
@NonNull Set<String> uris, @NonNull Set<String> uris,
@NonNull Map<String, List<String>> typePropertyPathsMap) { @NonNull Map<String, List<String>> typePropertyPathsMap) {
mNamespace = Preconditions.checkNotNull(namespace); mNamespace = Objects.requireNonNull(namespace);
mUris = Preconditions.checkNotNull(uris); mUris = Objects.requireNonNull(uris);
mTypePropertyPathsMap = Preconditions.checkNotNull(typePropertyPathsMap); mTypePropertyPathsMap = Objects.requireNonNull(typePropertyPathsMap);
} }
/** Returns the namespace attached to the request. */ /** Returns the namespace attached to the request. */
@@ -114,7 +115,7 @@ public final class GetByUriRequest {
/** Creates a {@link GetByUriRequest.Builder} instance. */ /** Creates a {@link GetByUriRequest.Builder} instance. */
public Builder(@NonNull String namespace) { public Builder(@NonNull String namespace) {
mNamespace = Preconditions.checkNotNull(namespace); mNamespace = Objects.requireNonNull(namespace);
} }
/** /**
@@ -124,7 +125,7 @@ public final class GetByUriRequest {
*/ */
@NonNull @NonNull
public Builder addUris(@NonNull String... uris) { public Builder addUris(@NonNull String... uris) {
Preconditions.checkNotNull(uris); Objects.requireNonNull(uris);
return addUris(Arrays.asList(uris)); return addUris(Arrays.asList(uris));
} }
@@ -136,7 +137,7 @@ public final class GetByUriRequest {
@NonNull @NonNull
public Builder addUris(@NonNull Collection<String> uris) { public Builder addUris(@NonNull Collection<String> uris) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(uris); Objects.requireNonNull(uris);
mUris.addAll(uris); mUris.addAll(uris);
return this; return this;
} }
@@ -161,11 +162,11 @@ public final class GetByUriRequest {
public Builder addProjection( public Builder addProjection(
@NonNull String schemaType, @NonNull Collection<String> propertyPaths) { @NonNull String schemaType, @NonNull Collection<String> propertyPaths) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(schemaType); Objects.requireNonNull(schemaType);
Preconditions.checkNotNull(propertyPaths); Objects.requireNonNull(propertyPaths);
List<String> propertyPathsList = new ArrayList<>(propertyPaths.size()); List<String> propertyPathsList = new ArrayList<>(propertyPaths.size());
for (String propertyPath : propertyPaths) { for (String propertyPath : propertyPaths) {
Preconditions.checkNotNull(propertyPath); Objects.requireNonNull(propertyPath);
propertyPathsList.add(propertyPath); propertyPathsList.add(propertyPath);
} }
mProjectionTypePropertyPaths.put(schemaType, propertyPathsList); mProjectionTypePropertyPaths.put(schemaType, propertyPathsList);

View File

@@ -24,6 +24,7 @@ import android.util.ArraySet;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Objects;
import java.util.Set; import java.util.Set;
/** The response class of {@link AppSearchSession#getSchema} */ /** The response class of {@link AppSearchSession#getSchema} */
@@ -34,7 +35,7 @@ public class GetSchemaResponse {
private final Bundle mBundle; private final Bundle mBundle;
GetSchemaResponse(@NonNull Bundle bundle) { GetSchemaResponse(@NonNull Bundle bundle) {
mBundle = Preconditions.checkNotNull(bundle); mBundle = Objects.requireNonNull(bundle);
} }
/** /**

View File

@@ -20,7 +20,7 @@ import android.annotation.NonNull;
import android.app.appsearch.util.BundleUtil; import android.app.appsearch.util.BundleUtil;
import android.os.Bundle; import android.os.Bundle;
import com.android.internal.util.Preconditions; import java.util.Objects;
/** This class represents a uniquely identifiable package. */ /** This class represents a uniquely identifiable package. */
public class PackageIdentifier { public class PackageIdentifier {
@@ -43,7 +43,7 @@ public class PackageIdentifier {
/** @hide */ /** @hide */
public PackageIdentifier(@NonNull Bundle bundle) { public PackageIdentifier(@NonNull Bundle bundle) {
mBundle = Preconditions.checkNotNull(bundle); mBundle = Objects.requireNonNull(bundle);
} }
/** @hide */ /** @hide */
@@ -54,12 +54,12 @@ public class PackageIdentifier {
@NonNull @NonNull
public String getPackageName() { public String getPackageName() {
return Preconditions.checkNotNull(mBundle.getString(PACKAGE_NAME_FIELD)); return Objects.requireNonNull(mBundle.getString(PACKAGE_NAME_FIELD));
} }
@NonNull @NonNull
public byte[] getSha256Certificate() { public byte[] getSha256Certificate() {
return Preconditions.checkNotNull(mBundle.getByteArray(SHA256_CERTIFICATE_FIELD)); return Objects.requireNonNull(mBundle.getByteArray(SHA256_CERTIFICATE_FIELD));
} }
@Override @Override

View File

@@ -26,6 +26,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
* Encapsulates a request to index documents into an {@link AppSearchSession} database. * Encapsulates a request to index documents into an {@link AppSearchSession} database.
@@ -61,7 +62,7 @@ public final class PutDocumentsRequest {
*/ */
@NonNull @NonNull
public Builder addGenericDocuments(@NonNull GenericDocument... documents) { public Builder addGenericDocuments(@NonNull GenericDocument... documents) {
Preconditions.checkNotNull(documents); Objects.requireNonNull(documents);
return addGenericDocuments(Arrays.asList(documents)); return addGenericDocuments(Arrays.asList(documents));
} }
@@ -74,7 +75,7 @@ public final class PutDocumentsRequest {
public Builder addGenericDocuments( public Builder addGenericDocuments(
@NonNull Collection<? extends GenericDocument> documents) { @NonNull Collection<? extends GenericDocument> documents) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(documents); Objects.requireNonNull(documents);
mDocuments.addAll(documents); mDocuments.addAll(documents);
return this; return this;
} }

View File

@@ -24,6 +24,7 @@ import com.android.internal.util.Preconditions;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Objects;
import java.util.Set; import java.util.Set;
/** /**
@@ -65,7 +66,7 @@ public final class RemoveByUriRequest {
/** Creates a {@link RemoveByUriRequest.Builder} instance. */ /** Creates a {@link RemoveByUriRequest.Builder} instance. */
public Builder(@NonNull String namespace) { public Builder(@NonNull String namespace) {
mNamespace = Preconditions.checkNotNull(namespace); mNamespace = Objects.requireNonNull(namespace);
} }
/** /**
@@ -75,7 +76,7 @@ public final class RemoveByUriRequest {
*/ */
@NonNull @NonNull
public Builder addUris(@NonNull String... uris) { public Builder addUris(@NonNull String... uris) {
Preconditions.checkNotNull(uris); Objects.requireNonNull(uris);
return addUris(Arrays.asList(uris)); return addUris(Arrays.asList(uris));
} }
@@ -87,7 +88,7 @@ public final class RemoveByUriRequest {
@NonNull @NonNull
public Builder addUris(@NonNull Collection<String> uris) { public Builder addUris(@NonNull Collection<String> uris) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(uris); Objects.requireNonNull(uris);
mUris.addAll(uris); mUris.addAll(uris);
return this; return this;
} }

View File

@@ -20,6 +20,8 @@ import android.annotation.NonNull;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import java.util.Objects;
/** /**
* A request to report usage of a document owned by another app from a system UI surface. * A request to report usage of a document owned by another app from a system UI surface.
* *
@@ -42,10 +44,10 @@ public final class ReportSystemUsageRequest {
@NonNull String namespace, @NonNull String namespace,
@NonNull String uri, @NonNull String uri,
long usageTimeMillis) { long usageTimeMillis) {
mPackageName = Preconditions.checkNotNull(packageName); mPackageName = Objects.requireNonNull(packageName);
mDatabase = Preconditions.checkNotNull(database); mDatabase = Objects.requireNonNull(database);
mNamespace = Preconditions.checkNotNull(namespace); mNamespace = Objects.requireNonNull(namespace);
mUri = Preconditions.checkNotNull(uri); mUri = Objects.requireNonNull(uri);
mUsageTimeMillis = usageTimeMillis; mUsageTimeMillis = usageTimeMillis;
} }
@@ -95,9 +97,9 @@ public final class ReportSystemUsageRequest {
/** Creates a {@link ReportSystemUsageRequest.Builder} instance. */ /** Creates a {@link ReportSystemUsageRequest.Builder} instance. */
public Builder( public Builder(
@NonNull String packageName, @NonNull String database, @NonNull String namespace) { @NonNull String packageName, @NonNull String database, @NonNull String namespace) {
mPackageName = Preconditions.checkNotNull(packageName); mPackageName = Objects.requireNonNull(packageName);
mDatabase = Preconditions.checkNotNull(database); mDatabase = Objects.requireNonNull(database);
mNamespace = Preconditions.checkNotNull(namespace); mNamespace = Objects.requireNonNull(namespace);
} }
/** /**
@@ -110,7 +112,7 @@ public final class ReportSystemUsageRequest {
@NonNull @NonNull
public ReportSystemUsageRequest.Builder setUri(@NonNull String uri) { public ReportSystemUsageRequest.Builder setUri(@NonNull String uri) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(uri); Objects.requireNonNull(uri);
mUri = uri; mUri = uri;
return this; return this;
} }
@@ -142,7 +144,7 @@ public final class ReportSystemUsageRequest {
@NonNull @NonNull
public ReportSystemUsageRequest build() { public ReportSystemUsageRequest build() {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(mUri, "ReportUsageRequest is missing a URI"); Objects.requireNonNull(mUri, "ReportUsageRequest is missing a URI");
if (mUsageTimeMillis == null) { if (mUsageTimeMillis == null) {
mUsageTimeMillis = System.currentTimeMillis(); mUsageTimeMillis = System.currentTimeMillis();
} }

View File

@@ -20,6 +20,8 @@ import android.annotation.NonNull;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import java.util.Objects;
/** /**
* A request to report usage of a document. * A request to report usage of a document.
* *
@@ -33,8 +35,8 @@ public final class ReportUsageRequest {
private final long mUsageTimeMillis; private final long mUsageTimeMillis;
ReportUsageRequest(@NonNull String namespace, @NonNull String uri, long usageTimeMillis) { ReportUsageRequest(@NonNull String namespace, @NonNull String uri, long usageTimeMillis) {
mNamespace = Preconditions.checkNotNull(namespace); mNamespace = Objects.requireNonNull(namespace);
mUri = Preconditions.checkNotNull(uri); mUri = Objects.requireNonNull(uri);
mUsageTimeMillis = usageTimeMillis; mUsageTimeMillis = usageTimeMillis;
} }
@@ -69,7 +71,7 @@ public final class ReportUsageRequest {
/** Creates a {@link ReportUsageRequest.Builder} instance. */ /** Creates a {@link ReportUsageRequest.Builder} instance. */
public Builder(@NonNull String namespace) { public Builder(@NonNull String namespace) {
mNamespace = Preconditions.checkNotNull(namespace); mNamespace = Objects.requireNonNull(namespace);
} }
/** /**
@@ -82,7 +84,7 @@ public final class ReportUsageRequest {
@NonNull @NonNull
public ReportUsageRequest.Builder setUri(@NonNull String uri) { public ReportUsageRequest.Builder setUri(@NonNull String uri) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(uri); Objects.requireNonNull(uri);
mUri = uri; mUri = uri;
return this; return this;
} }
@@ -114,7 +116,7 @@ public final class ReportUsageRequest {
@NonNull @NonNull
public ReportUsageRequest build() { public ReportUsageRequest build() {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(mUri, "ReportUsageRequest is missing a URI"); Objects.requireNonNull(mUri, "ReportUsageRequest is missing a URI");
if (mUsageTimeMillis == null) { if (mUsageTimeMillis == null) {
mUsageTimeMillis = System.currentTimeMillis(); mUsageTimeMillis = System.currentTimeMillis();
} }

View File

@@ -59,7 +59,7 @@ public final class SearchResult {
/** @hide */ /** @hide */
public SearchResult(@NonNull Bundle bundle) { public SearchResult(@NonNull Bundle bundle) {
mBundle = Preconditions.checkNotNull(bundle); mBundle = Objects.requireNonNull(bundle);
} }
/** @hide */ /** @hide */
@@ -77,8 +77,7 @@ public final class SearchResult {
public GenericDocument getGenericDocument() { public GenericDocument getGenericDocument() {
if (mDocument == null) { if (mDocument == null) {
mDocument = mDocument =
new GenericDocument( new GenericDocument(Objects.requireNonNull(mBundle.getBundle(DOCUMENT_FIELD)));
Preconditions.checkNotNull(mBundle.getBundle(DOCUMENT_FIELD)));
} }
return mDocument; return mDocument;
} }
@@ -95,7 +94,7 @@ public final class SearchResult {
public List<MatchInfo> getMatches() { public List<MatchInfo> getMatches() {
if (mMatches == null) { if (mMatches == null) {
List<Bundle> matchBundles = List<Bundle> matchBundles =
Preconditions.checkNotNull(mBundle.getParcelableArrayList(MATCHES_FIELD)); Objects.requireNonNull(mBundle.getParcelableArrayList(MATCHES_FIELD));
mMatches = new ArrayList<>(matchBundles.size()); mMatches = new ArrayList<>(matchBundles.size());
for (int i = 0; i < matchBundles.size(); i++) { for (int i = 0; i < matchBundles.size(); i++) {
MatchInfo matchInfo = new MatchInfo(matchBundles.get(i), getGenericDocument()); MatchInfo matchInfo = new MatchInfo(matchBundles.get(i), getGenericDocument());
@@ -112,7 +111,7 @@ public final class SearchResult {
*/ */
@NonNull @NonNull
public String getPackageName() { public String getPackageName() {
return Preconditions.checkNotNull(mBundle.getString(PACKAGE_NAME_FIELD)); return Objects.requireNonNull(mBundle.getString(PACKAGE_NAME_FIELD));
} }
/** /**
@@ -122,7 +121,7 @@ public final class SearchResult {
*/ */
@NonNull @NonNull
public String getDatabaseName() { public String getDatabaseName() {
return Preconditions.checkNotNull(mBundle.getString(DATABASE_NAME_FIELD)); return Objects.requireNonNull(mBundle.getString(DATABASE_NAME_FIELD));
} }
/** /**
@@ -169,8 +168,8 @@ public final class SearchResult {
* @param databaseName the database name the matched document belongs to. * @param databaseName the database name the matched document belongs to.
*/ */
public Builder(@NonNull String packageName, @NonNull String databaseName) { public Builder(@NonNull String packageName, @NonNull String databaseName) {
mBundle.putString(PACKAGE_NAME_FIELD, Preconditions.checkNotNull(packageName)); mBundle.putString(PACKAGE_NAME_FIELD, Objects.requireNonNull(packageName));
mBundle.putString(DATABASE_NAME_FIELD, Preconditions.checkNotNull(databaseName)); mBundle.putString(DATABASE_NAME_FIELD, Objects.requireNonNull(databaseName));
} }
/** /**
@@ -312,9 +311,9 @@ public final class SearchResult {
@Nullable private MatchRange mWindowRange; @Nullable private MatchRange mWindowRange;
MatchInfo(@NonNull Bundle bundle, @Nullable GenericDocument document) { MatchInfo(@NonNull Bundle bundle, @Nullable GenericDocument document) {
mBundle = Preconditions.checkNotNull(bundle); mBundle = Objects.requireNonNull(bundle);
mDocument = document; mDocument = document;
mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD)); mPropertyPath = Objects.requireNonNull(bundle.getString(PROPERTY_PATH_FIELD));
} }
/** /**
@@ -449,7 +448,7 @@ public final class SearchResult {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mBundle.putString( mBundle.putString(
SearchResult.MatchInfo.PROPERTY_PATH_FIELD, SearchResult.MatchInfo.PROPERTY_PATH_FIELD,
Preconditions.checkNotNull(propertyPath)); Objects.requireNonNull(propertyPath));
return this; return this;
} }
@@ -461,7 +460,7 @@ public final class SearchResult {
@NonNull @NonNull
public Builder setExactMatchRange(@NonNull MatchRange matchRange) { public Builder setExactMatchRange(@NonNull MatchRange matchRange) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(matchRange); Objects.requireNonNull(matchRange);
mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_LOWER_FIELD, matchRange.getStart()); mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_LOWER_FIELD, matchRange.getStart());
mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_UPPER_FIELD, matchRange.getEnd()); mBundle.putInt(MatchInfo.EXACT_MATCH_RANGE_UPPER_FIELD, matchRange.getEnd());
return this; return this;
@@ -475,7 +474,7 @@ public final class SearchResult {
@NonNull @NonNull
public Builder setSnippetRange(@NonNull MatchRange matchRange) { public Builder setSnippetRange(@NonNull MatchRange matchRange) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(matchRange); Objects.requireNonNull(matchRange);
mBundle.putInt(MatchInfo.SNIPPET_RANGE_LOWER_FIELD, matchRange.getStart()); mBundle.putInt(MatchInfo.SNIPPET_RANGE_LOWER_FIELD, matchRange.getStart());
mBundle.putInt(MatchInfo.SNIPPET_RANGE_UPPER_FIELD, matchRange.getEnd()); mBundle.putInt(MatchInfo.SNIPPET_RANGE_UPPER_FIELD, matchRange.getEnd());
return this; return this;

View File

@@ -20,11 +20,10 @@ import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.os.Bundle; import android.os.Bundle;
import com.android.internal.util.Preconditions;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
* This class represents a page of {@link SearchResult}s * This class represents a page of {@link SearchResult}s
@@ -41,7 +40,7 @@ public class SearchResultPage {
@NonNull private final Bundle mBundle; @NonNull private final Bundle mBundle;
public SearchResultPage(@NonNull Bundle bundle) { public SearchResultPage(@NonNull Bundle bundle) {
mBundle = Preconditions.checkNotNull(bundle); mBundle = Objects.requireNonNull(bundle);
mNextPageToken = mBundle.getLong(NEXT_PAGE_TOKEN_FIELD); mNextPageToken = mBundle.getLong(NEXT_PAGE_TOKEN_FIELD);
} }

View File

@@ -34,6 +34,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
/** /**
@@ -176,7 +177,7 @@ public final class SearchSpec {
/** @hide */ /** @hide */
public SearchSpec(@NonNull Bundle bundle) { public SearchSpec(@NonNull Bundle bundle) {
Preconditions.checkNotNull(bundle); Objects.requireNonNull(bundle);
mBundle = bundle; mBundle = bundle;
} }
@@ -342,7 +343,7 @@ public final class SearchSpec {
*/ */
@NonNull @NonNull
public Builder addFilterSchemas(@NonNull String... schemas) { public Builder addFilterSchemas(@NonNull String... schemas) {
Preconditions.checkNotNull(schemas); Objects.requireNonNull(schemas);
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
return addFilterSchemas(Arrays.asList(schemas)); return addFilterSchemas(Arrays.asList(schemas));
} }
@@ -355,7 +356,7 @@ public final class SearchSpec {
*/ */
@NonNull @NonNull
public Builder addFilterSchemas(@NonNull Collection<String> schemas) { public Builder addFilterSchemas(@NonNull Collection<String> schemas) {
Preconditions.checkNotNull(schemas); Objects.requireNonNull(schemas);
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mSchemas.addAll(schemas); mSchemas.addAll(schemas);
return this; return this;
@@ -369,7 +370,7 @@ public final class SearchSpec {
*/ */
@NonNull @NonNull
public Builder addFilterNamespaces(@NonNull String... namespaces) { public Builder addFilterNamespaces(@NonNull String... namespaces) {
Preconditions.checkNotNull(namespaces); Objects.requireNonNull(namespaces);
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
return addFilterNamespaces(Arrays.asList(namespaces)); return addFilterNamespaces(Arrays.asList(namespaces));
} }
@@ -382,7 +383,7 @@ public final class SearchSpec {
*/ */
@NonNull @NonNull
public Builder addFilterNamespaces(@NonNull Collection<String> namespaces) { public Builder addFilterNamespaces(@NonNull Collection<String> namespaces) {
Preconditions.checkNotNull(namespaces); Objects.requireNonNull(namespaces);
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mNamespaces.addAll(namespaces); mNamespaces.addAll(namespaces);
return this; return this;
@@ -398,7 +399,7 @@ public final class SearchSpec {
*/ */
@NonNull @NonNull
public Builder addFilterPackageNames(@NonNull String... packageNames) { public Builder addFilterPackageNames(@NonNull String... packageNames) {
Preconditions.checkNotNull(packageNames); Objects.requireNonNull(packageNames);
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
return addFilterPackageNames(Arrays.asList(packageNames)); return addFilterPackageNames(Arrays.asList(packageNames));
} }
@@ -413,7 +414,7 @@ public final class SearchSpec {
*/ */
@NonNull @NonNull
public Builder addFilterPackageNames(@NonNull Collection<String> packageNames) { public Builder addFilterPackageNames(@NonNull Collection<String> packageNames) {
Preconditions.checkNotNull(packageNames); Objects.requireNonNull(packageNames);
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mPackageNames.addAll(packageNames); mPackageNames.addAll(packageNames);
return this; return this;
@@ -586,11 +587,11 @@ public final class SearchSpec {
public SearchSpec.Builder addProjection( public SearchSpec.Builder addProjection(
@NonNull String schema, @NonNull Collection<String> propertyPaths) { @NonNull String schema, @NonNull Collection<String> propertyPaths) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(schema); Objects.requireNonNull(schema);
Preconditions.checkNotNull(propertyPaths); Objects.requireNonNull(propertyPaths);
ArrayList<String> propertyPathsArrayList = new ArrayList<>(propertyPaths.size()); ArrayList<String> propertyPathsArrayList = new ArrayList<>(propertyPaths.size());
for (String propertyPath : propertyPaths) { for (String propertyPath : propertyPaths) {
Preconditions.checkNotNull(propertyPath); Objects.requireNonNull(propertyPath);
propertyPathsArrayList.add(propertyPath); propertyPathsArrayList.add(propertyPath);
} }
mProjectionTypePropertyMasks.putStringArrayList(schema, propertyPathsArrayList); mProjectionTypePropertyMasks.putStringArrayList(schema, propertyPathsArrayList);

View File

@@ -28,6 +28,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
/** /**
@@ -94,10 +95,10 @@ public final class SetSchemaRequest {
@NonNull Map<String, Migrator> migrators, @NonNull Map<String, Migrator> migrators,
boolean forceOverride, boolean forceOverride,
int version) { int version) {
mSchemas = Preconditions.checkNotNull(schemas); mSchemas = Objects.requireNonNull(schemas);
mSchemasNotDisplayedBySystem = Preconditions.checkNotNull(schemasNotDisplayedBySystem); mSchemasNotDisplayedBySystem = Objects.requireNonNull(schemasNotDisplayedBySystem);
mSchemasVisibleToPackages = Preconditions.checkNotNull(schemasVisibleToPackages); mSchemasVisibleToPackages = Objects.requireNonNull(schemasVisibleToPackages);
mMigrators = Preconditions.checkNotNull(migrators); mMigrators = Objects.requireNonNull(migrators);
mForceOverride = forceOverride; mForceOverride = forceOverride;
mVersion = version; mVersion = version;
} }
@@ -192,7 +193,7 @@ public final class SetSchemaRequest {
*/ */
@NonNull @NonNull
public Builder addSchemas(@NonNull AppSearchSchema... schemas) { public Builder addSchemas(@NonNull AppSearchSchema... schemas) {
Preconditions.checkNotNull(schemas); Objects.requireNonNull(schemas);
return addSchemas(Arrays.asList(schemas)); return addSchemas(Arrays.asList(schemas));
} }
@@ -206,7 +207,7 @@ public final class SetSchemaRequest {
@NonNull @NonNull
public Builder addSchemas(@NonNull Collection<AppSearchSchema> schemas) { public Builder addSchemas(@NonNull Collection<AppSearchSchema> schemas) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkNotNull(schemas); Objects.requireNonNull(schemas);
mSchemas.addAll(schemas); mSchemas.addAll(schemas);
return this; return this;
} }
@@ -231,7 +232,7 @@ public final class SetSchemaRequest {
@NonNull @NonNull
public Builder setSchemaTypeDisplayedBySystem( public Builder setSchemaTypeDisplayedBySystem(
@NonNull String schemaType, boolean displayed) { @NonNull String schemaType, boolean displayed) {
Preconditions.checkNotNull(schemaType); Objects.requireNonNull(schemaType);
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
if (displayed) { if (displayed) {
@@ -270,8 +271,8 @@ public final class SetSchemaRequest {
@NonNull String schemaType, @NonNull String schemaType,
boolean visible, boolean visible,
@NonNull PackageIdentifier packageIdentifier) { @NonNull PackageIdentifier packageIdentifier) {
Preconditions.checkNotNull(schemaType); Objects.requireNonNull(schemaType);
Preconditions.checkNotNull(packageIdentifier); Objects.requireNonNull(packageIdentifier);
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Set<PackageIdentifier> packageIdentifiers = mSchemasVisibleToPackages.get(schemaType); Set<PackageIdentifier> packageIdentifiers = mSchemasVisibleToPackages.get(schemaType);
@@ -321,8 +322,8 @@ public final class SetSchemaRequest {
@NonNull @NonNull
@SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects. @SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects.
public Builder setMigrator(@NonNull String schemaType, @NonNull Migrator migrator) { public Builder setMigrator(@NonNull String schemaType, @NonNull Migrator migrator) {
Preconditions.checkNotNull(schemaType); Objects.requireNonNull(schemaType);
Preconditions.checkNotNull(migrator); Objects.requireNonNull(migrator);
mMigrators.put(schemaType, migrator); mMigrators.put(schemaType, migrator);
return this; return this;
} }
@@ -350,7 +351,7 @@ public final class SetSchemaRequest {
*/ */
@NonNull @NonNull
public Builder setMigrators(@NonNull Map<String, Migrator> migrators) { public Builder setMigrators(@NonNull Map<String, Migrator> migrators) {
Preconditions.checkNotNull(migrators); Objects.requireNonNull(migrators);
mMigrators.putAll(migrators); mMigrators.putAll(migrators);
return this; return this;
} }

View File

@@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set; import java.util.Set;
/** The response class of {@link AppSearchSession#setSchema} */ /** The response class of {@link AppSearchSession#setSchema} */
@@ -61,8 +62,8 @@ public class SetSchemaResponse {
@Nullable private Set<String> mIncompatibleTypes; @Nullable private Set<String> mIncompatibleTypes;
SetSchemaResponse(@NonNull Bundle bundle, @NonNull List<MigrationFailure> migrationFailures) { SetSchemaResponse(@NonNull Bundle bundle, @NonNull List<MigrationFailure> migrationFailures) {
mBundle = Preconditions.checkNotNull(bundle); mBundle = Objects.requireNonNull(bundle);
mMigrationFailures = Preconditions.checkNotNull(migrationFailures); mMigrationFailures = Objects.requireNonNull(migrationFailures);
} }
SetSchemaResponse(@NonNull Bundle bundle) { SetSchemaResponse(@NonNull Bundle bundle) {
@@ -103,7 +104,7 @@ public class SetSchemaResponse {
if (mDeletedTypes == null) { if (mDeletedTypes == null) {
mDeletedTypes = mDeletedTypes =
new ArraySet<>( new ArraySet<>(
Preconditions.checkNotNull( Objects.requireNonNull(
mBundle.getStringArrayList(DELETED_TYPES_FIELD))); mBundle.getStringArrayList(DELETED_TYPES_FIELD)));
} }
return Collections.unmodifiableSet(mDeletedTypes); return Collections.unmodifiableSet(mDeletedTypes);
@@ -118,7 +119,7 @@ public class SetSchemaResponse {
if (mMigratedTypes == null) { if (mMigratedTypes == null) {
mMigratedTypes = mMigratedTypes =
new ArraySet<>( new ArraySet<>(
Preconditions.checkNotNull( Objects.requireNonNull(
mBundle.getStringArrayList(MIGRATED_TYPES_FIELD))); mBundle.getStringArrayList(MIGRATED_TYPES_FIELD)));
} }
return Collections.unmodifiableSet(mMigratedTypes); return Collections.unmodifiableSet(mMigratedTypes);
@@ -139,7 +140,7 @@ public class SetSchemaResponse {
if (mIncompatibleTypes == null) { if (mIncompatibleTypes == null) {
mIncompatibleTypes = mIncompatibleTypes =
new ArraySet<>( new ArraySet<>(
Preconditions.checkNotNull( Objects.requireNonNull(
mBundle.getStringArrayList(INCOMPATIBLE_TYPES_FIELD))); mBundle.getStringArrayList(INCOMPATIBLE_TYPES_FIELD)));
} }
return Collections.unmodifiableSet(mIncompatibleTypes); return Collections.unmodifiableSet(mIncompatibleTypes);
@@ -173,7 +174,7 @@ public class SetSchemaResponse {
public Builder addMigrationFailures( public Builder addMigrationFailures(
@NonNull Collection<MigrationFailure> migrationFailures) { @NonNull Collection<MigrationFailure> migrationFailures) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mMigrationFailures.addAll(Preconditions.checkNotNull(migrationFailures)); mMigrationFailures.addAll(Objects.requireNonNull(migrationFailures));
return this; return this;
} }
@@ -181,7 +182,7 @@ public class SetSchemaResponse {
@NonNull @NonNull
public Builder addMigrationFailure(@NonNull MigrationFailure migrationFailure) { public Builder addMigrationFailure(@NonNull MigrationFailure migrationFailure) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mMigrationFailures.add(Preconditions.checkNotNull(migrationFailure)); mMigrationFailures.add(Objects.requireNonNull(migrationFailure));
return this; return this;
} }
@@ -189,7 +190,7 @@ public class SetSchemaResponse {
@NonNull @NonNull
public Builder addDeletedTypes(@NonNull Collection<String> deletedTypes) { public Builder addDeletedTypes(@NonNull Collection<String> deletedTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mDeletedTypes.addAll(Preconditions.checkNotNull(deletedTypes)); mDeletedTypes.addAll(Objects.requireNonNull(deletedTypes));
return this; return this;
} }
@@ -197,7 +198,7 @@ public class SetSchemaResponse {
@NonNull @NonNull
public Builder addDeletedType(@NonNull String deletedType) { public Builder addDeletedType(@NonNull String deletedType) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mDeletedTypes.add(Preconditions.checkNotNull(deletedType)); mDeletedTypes.add(Objects.requireNonNull(deletedType));
return this; return this;
} }
@@ -205,7 +206,7 @@ public class SetSchemaResponse {
@NonNull @NonNull
public Builder addIncompatibleTypes(@NonNull Collection<String> incompatibleTypes) { public Builder addIncompatibleTypes(@NonNull Collection<String> incompatibleTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mIncompatibleTypes.addAll(Preconditions.checkNotNull(incompatibleTypes)); mIncompatibleTypes.addAll(Objects.requireNonNull(incompatibleTypes));
return this; return this;
} }
@@ -213,7 +214,7 @@ public class SetSchemaResponse {
@NonNull @NonNull
public Builder addIncompatibleType(@NonNull String incompatibleType) { public Builder addIncompatibleType(@NonNull String incompatibleType) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mIncompatibleTypes.add(Preconditions.checkNotNull(incompatibleType)); mIncompatibleTypes.add(Objects.requireNonNull(incompatibleType));
return this; return this;
} }
@@ -221,7 +222,7 @@ public class SetSchemaResponse {
@NonNull @NonNull
public Builder addMigratedTypes(@NonNull Collection<String> migratedTypes) { public Builder addMigratedTypes(@NonNull Collection<String> migratedTypes) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mMigratedTypes.addAll(Preconditions.checkNotNull(migratedTypes)); mMigratedTypes.addAll(Objects.requireNonNull(migratedTypes));
return this; return this;
} }
@@ -229,7 +230,7 @@ public class SetSchemaResponse {
@NonNull @NonNull
public Builder addMigratedType(@NonNull String migratedType) { public Builder addMigratedType(@NonNull String migratedType) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mMigratedTypes.add(Preconditions.checkNotNull(migratedType)); mMigratedTypes.add(Objects.requireNonNull(migratedType));
return this; return this;
} }
@@ -318,7 +319,7 @@ public class SetSchemaResponse {
@NonNull @NonNull
public Builder setSchemaType(@NonNull String schemaType) { public Builder setSchemaType(@NonNull String schemaType) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mSchemaType = Preconditions.checkNotNull(schemaType); mSchemaType = Objects.requireNonNull(schemaType);
return this; return this;
} }
@@ -326,7 +327,7 @@ public class SetSchemaResponse {
@NonNull @NonNull
public Builder setNamespace(@NonNull String namespace) { public Builder setNamespace(@NonNull String namespace) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mNamespace = Preconditions.checkNotNull(namespace); mNamespace = Objects.requireNonNull(namespace);
return this; return this;
} }
@@ -334,7 +335,7 @@ public class SetSchemaResponse {
@NonNull @NonNull
public Builder setUri(@NonNull String uri) { public Builder setUri(@NonNull String uri) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
mUri = Preconditions.checkNotNull(uri); mUri = Objects.requireNonNull(uri);
return this; return this;
} }
@@ -343,7 +344,7 @@ public class SetSchemaResponse {
public Builder setAppSearchResult(@NonNull AppSearchResult<Void> appSearchResult) { public Builder setAppSearchResult(@NonNull AppSearchResult<Void> appSearchResult) {
Preconditions.checkState(!mBuilt, "Builder has already been used"); Preconditions.checkState(!mBuilt, "Builder has already been used");
Preconditions.checkState(!appSearchResult.isSuccess(), "Input a success result"); Preconditions.checkState(!appSearchResult.isSuccess(), "Input a success result");
mFailureResult = Preconditions.checkNotNull(appSearchResult); mFailureResult = Objects.requireNonNull(appSearchResult);
return this; return this;
} }

View File

@@ -21,6 +21,8 @@ import android.os.Bundle;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import java.util.Objects;
/** The response class of {@code AppSearchSession#getStorageInfo}. */ /** The response class of {@code AppSearchSession#getStorageInfo}. */
public class StorageInfo { public class StorageInfo {
@@ -31,7 +33,7 @@ public class StorageInfo {
private final Bundle mBundle; private final Bundle mBundle;
StorageInfo(@NonNull Bundle bundle) { StorageInfo(@NonNull Bundle bundle) {
mBundle = Preconditions.checkNotNull(bundle); mBundle = Objects.requireNonNull(bundle);
} }
/** /**

View File

@@ -37,7 +37,11 @@ import java.util.Set;
public final class SchemaMigrationUtil { public final class SchemaMigrationUtil {
private SchemaMigrationUtil() {} private SchemaMigrationUtil() {}
/** Returns all active {@link Migrator}s that need to be triggered in this migration. */ /**
* Returns all active {@link Migrator}s that need to be triggered in this migration.
*
* <p>{@link Migrator#shouldMigrate} returns {@code true} will make the {@link Migrator} active.
*/
@NonNull @NonNull
public static Map<String, Migrator> getActiveMigrators( public static Map<String, Migrator> getActiveMigrators(
@NonNull Set<AppSearchSchema> existingSchemas, @NonNull Set<AppSearchSchema> existingSchemas,

View File

@@ -64,6 +64,8 @@ import com.android.server.appsearch.external.localstorage.stats.CallStats;
import com.android.server.appsearch.stats.LoggerInstanceManager; import com.android.server.appsearch.stats.LoggerInstanceManager;
import com.android.server.appsearch.stats.PlatformLogger; import com.android.server.appsearch.stats.PlatformLogger;
import com.google.android.icing.proto.PersistType;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.EOFException; import java.io.EOFException;
@@ -364,6 +366,8 @@ public class AppSearchManagerService extends SystemService {
++operationFailureCount; ++operationFailureCount;
} }
} }
// Now that the batch has been written. Persist the newly written data.
impl.persistToDisk(PersistType.Code.LITE);
invokeCallbackOnResult(callback, resultBuilder.build()); invokeCallbackOnResult(callback, resultBuilder.build());
} catch (Throwable t) { } catch (Throwable t) {
invokeCallbackOnError(callback, t); invokeCallbackOnError(callback, t);
@@ -631,7 +635,7 @@ public class AppSearchManagerService extends SystemService {
} }
} }
} }
impl.persistToDisk(); impl.persistToDisk(PersistType.Code.FULL);
invokeCallbackOnResult(callback, invokeCallbackOnResult(callback,
AppSearchResult.newSuccessfulResult(migrationFailureBundles)); AppSearchResult.newSuccessfulResult(migrationFailureBundles));
} catch (Throwable t) { } catch (Throwable t) {
@@ -708,6 +712,8 @@ public class AppSearchManagerService extends SystemService {
resultBuilder.setResult(uri, throwableToFailedResult(t)); resultBuilder.setResult(uri, throwableToFailedResult(t));
} }
} }
// Now that the batch has been written. Persist the newly written data.
impl.persistToDisk(PersistType.Code.LITE);
invokeCallbackOnResult(callback, resultBuilder.build()); invokeCallbackOnResult(callback, resultBuilder.build());
} catch (Throwable t) { } catch (Throwable t) {
invokeCallbackOnError(callback, t); invokeCallbackOnError(callback, t);
@@ -741,6 +747,8 @@ public class AppSearchManagerService extends SystemService {
databaseName, databaseName,
queryExpression, queryExpression,
new SearchSpec(searchSpecBundle)); new SearchSpec(searchSpecBundle));
// Now that the batch has been written. Persist the newly written data.
impl.persistToDisk(PersistType.Code.LITE);
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
} catch (Throwable t) { } catch (Throwable t) {
invokeCallbackOnError(callback, t); invokeCallbackOnError(callback, t);
@@ -785,7 +793,7 @@ public class AppSearchManagerService extends SystemService {
verifyUserUnlocked(callingUserId); verifyUserUnlocked(callingUserId);
AppSearchImpl impl = AppSearchImpl impl =
mImplInstanceManager.getAppSearchImpl(callingUserId); mImplInstanceManager.getAppSearchImpl(callingUserId);
impl.persistToDisk(); impl.persistToDisk(PersistType.Code.FULL);
} catch (Throwable t) { } catch (Throwable t) {
Log.e(TAG, "Unable to persist the data to disk", t); Log.e(TAG, "Unable to persist the data to disk", t);
} }

View File

@@ -36,6 +36,7 @@ import android.util.ArraySet;
import android.util.Log; import android.util.Log;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -153,7 +154,7 @@ public class VisibilityStore {
* AppSearchImpl. * AppSearchImpl.
*/ */
static final String VISIBILITY_STORE_PREFIX = static final String VISIBILITY_STORE_PREFIX =
AppSearchImpl.createPrefix(PACKAGE_NAME, DATABASE_NAME); PrefixUtil.createPrefix(PACKAGE_NAME, DATABASE_NAME);
/** Namespace of documents that contain visibility settings */ /** Namespace of documents that contain visibility settings */
private static final String NAMESPACE = ""; private static final String NAMESPACE = "";

View File

@@ -16,10 +16,18 @@
package com.android.server.appsearch.external.localstorage; package com.android.server.appsearch.external.localstorage;
import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.addPrefixToDocument;
import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPackagePrefix;
import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix;
import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getDatabaseName;
import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getPackageName;
import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getPrefix;
import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.removePrefix;
import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.removePrefixesFromDocument;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.annotation.WorkerThread; import android.annotation.WorkerThread;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema; import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument; import android.app.appsearch.GenericDocument;
import android.app.appsearch.GetByUriRequest; import android.app.appsearch.GetByUriRequest;
@@ -39,7 +47,6 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter; import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.ResultCodeToProtoConverter; import com.android.server.appsearch.external.localstorage.converter.ResultCodeToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter; import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
@@ -66,7 +73,6 @@ import com.google.android.icing.proto.OptimizeResultProto;
import com.google.android.icing.proto.PersistToDiskResultProto; import com.google.android.icing.proto.PersistToDiskResultProto;
import com.google.android.icing.proto.PersistType; import com.google.android.icing.proto.PersistType;
import com.google.android.icing.proto.PropertyConfigProto; import com.google.android.icing.proto.PropertyConfigProto;
import com.google.android.icing.proto.PropertyProto;
import com.google.android.icing.proto.PutResultProto; import com.google.android.icing.proto.PutResultProto;
import com.google.android.icing.proto.ReportUsageResultProto; import com.google.android.icing.proto.ReportUsageResultProto;
import com.google.android.icing.proto.ResetResultProto; import com.google.android.icing.proto.ResetResultProto;
@@ -89,6 +95,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -132,10 +139,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
public final class AppSearchImpl implements Closeable { public final class AppSearchImpl implements Closeable {
private static final String TAG = "AppSearchImpl"; private static final String TAG = "AppSearchImpl";
@VisibleForTesting static final char DATABASE_DELIMITER = '/';
@VisibleForTesting static final char PACKAGE_DELIMITER = '$';
@VisibleForTesting static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000; @VisibleForTesting static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000;
@VisibleForTesting static final int OPTIMIZE_THRESHOLD_BYTES = 1_000_000; // 1MB @VisibleForTesting static final int OPTIMIZE_THRESHOLD_BYTES = 1_000_000; // 1MB
@VisibleForTesting static final int CHECK_OPTIMIZE_INTERVAL = 100; @VisibleForTesting static final int CHECK_OPTIMIZE_INTERVAL = 100;
@@ -148,11 +151,12 @@ public final class AppSearchImpl implements Closeable {
@GuardedBy("mReadWriteLock") @GuardedBy("mReadWriteLock")
private final VisibilityStore mVisibilityStoreLocked; private final VisibilityStore mVisibilityStoreLocked;
// This map contains schemaTypes for all package-database prefixes. All values in the map are // This map contains schema types and SchemaTypeConfigProtos for all package-database
// prefixed with the package-database prefix. // prefixes. It maps each package-database prefix to an inner-map. The inner-map maps each
// TODO(b/172360376): Check if this can be replaced with an ArrayMap // prefixed schema type to its respective SchemaTypeConfigProto.
@GuardedBy("mReadWriteLock") @GuardedBy("mReadWriteLock")
private final Map<String, Set<String>> mSchemaMapLocked = new HashMap<>(); private final Map<String, Map<String, SchemaTypeConfigProto>> mSchemaMapLocked =
new ArrayMap<>();
// This map contains namespaces for all package-database prefixes. All values in the map are // This map contains namespaces for all package-database prefixes. All values in the map are
// prefixed with the package-database prefix. // prefixed with the package-database prefix.
@@ -182,9 +186,9 @@ public final class AppSearchImpl implements Closeable {
int userId, int userId,
@NonNull String globalQuerierPackage) @NonNull String globalQuerierPackage)
throws AppSearchException { throws AppSearchException {
Preconditions.checkNotNull(icingDir); Objects.requireNonNull(icingDir);
Preconditions.checkNotNull(context); Objects.requireNonNull(context);
Preconditions.checkNotNull(globalQuerierPackage); Objects.requireNonNull(globalQuerierPackage);
AppSearchImpl appSearchImpl = AppSearchImpl appSearchImpl =
new AppSearchImpl(icingDir, context, userId, globalQuerierPackage); new AppSearchImpl(icingDir, context, userId, globalQuerierPackage);
appSearchImpl.initializeVisibilityStore(); appSearchImpl.initializeVisibilityStore();
@@ -229,7 +233,7 @@ public final class AppSearchImpl implements Closeable {
// Populate schema map // Populate schema map
for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) { for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) {
String prefixedSchemaType = schema.getSchemaType(); String prefixedSchemaType = schema.getSchemaType();
addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType), prefixedSchemaType); addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType), schema);
} }
// Populate namespace map // Populate namespace map
@@ -278,7 +282,7 @@ public final class AppSearchImpl implements Closeable {
return; return;
} }
persistToDisk(); persistToDisk(PersistType.Code.FULL);
mIcingSearchEngineLocked.close(); mIcingSearchEngineLocked.close();
mClosedLocked = true; mClosedLocked = true;
} catch (AppSearchException e) { } catch (AppSearchException e) {
@@ -364,7 +368,14 @@ public final class AppSearchImpl implements Closeable {
} }
// Update derived data structures. // Update derived data structures.
mSchemaMapLocked.put(prefix, rewrittenSchemaResults.mRewrittenPrefixedTypes); for (SchemaTypeConfigProto schemaTypeConfigProto :
rewrittenSchemaResults.mRewrittenPrefixedTypes.values()) {
addToMap(mSchemaMapLocked, prefix, schemaTypeConfigProto);
}
for (String schemaType : rewrittenSchemaResults.mDeletedPrefixedTypes) {
removeFromMap(mSchemaMapLocked, prefix, schemaType);
}
Set<String> prefixedSchemasNotPlatformSurfaceable = Set<String> prefixedSchemasNotPlatformSurfaceable =
new ArraySet<>(schemasNotPlatformSurfaceable.size()); new ArraySet<>(schemasNotPlatformSurfaceable.size());
@@ -586,7 +597,7 @@ public final class AppSearchImpl implements Closeable {
mReadWriteLock.readLock().lock(); mReadWriteLock.readLock().lock();
try { try {
throwIfClosedLocked(); throwIfClosedLocked();
String prefix = createPrefix(packageName, databaseName);
List<TypePropertyMask> nonPrefixedPropertyMasks = List<TypePropertyMask> nonPrefixedPropertyMasks =
TypePropertyPathToProtoConverter.toTypePropertyMaskList(typePropertyPaths); TypePropertyPathToProtoConverter.toTypePropertyMaskList(typePropertyPaths);
List<TypePropertyMask> prefixedPropertyMasks = List<TypePropertyMask> prefixedPropertyMasks =
@@ -597,7 +608,7 @@ public final class AppSearchImpl implements Closeable {
String prefixedType = String prefixedType =
nonPrefixedType.equals(GetByUriRequest.PROJECTION_SCHEMA_TYPE_WILDCARD) nonPrefixedType.equals(GetByUriRequest.PROJECTION_SCHEMA_TYPE_WILDCARD)
? nonPrefixedType ? nonPrefixedType
: createPrefix(packageName, databaseName) + nonPrefixedType; : prefix + nonPrefixedType;
prefixedPropertyMasks.add( prefixedPropertyMasks.add(
typePropertyMask.toBuilder().setSchemaType(prefixedType).build()); typePropertyMask.toBuilder().setSchemaType(prefixedType).build());
} }
@@ -607,15 +618,17 @@ public final class AppSearchImpl implements Closeable {
.build(); .build();
GetResultProto getResultProto = GetResultProto getResultProto =
mIcingSearchEngineLocked.get( mIcingSearchEngineLocked.get(prefix + namespace, uri, getResultSpec);
createPrefix(packageName, databaseName) + namespace,
uri,
getResultSpec);
checkSuccess(getResultProto.getStatus()); checkSuccess(getResultProto.getStatus());
// The schema type map cannot be null at this point. It could only be null if no
// schema had ever been set for that prefix. Given we have retrieved a document from
// the index, we know a schema had to have been set.
Map<String, SchemaTypeConfigProto> schemaTypeMap = mSchemaMapLocked.get(prefix);
DocumentProto.Builder documentBuilder = getResultProto.getDocument().toBuilder(); DocumentProto.Builder documentBuilder = getResultProto.getDocument().toBuilder();
removePrefixesFromDocument(documentBuilder); removePrefixesFromDocument(documentBuilder);
return GenericDocumentToProtoConverter.toGenericDocument(documentBuilder.build()); return GenericDocumentToProtoConverter.toGenericDocument(
documentBuilder.build(), prefix, schemaTypeMap);
} finally { } finally {
mReadWriteLock.readLock().unlock(); mReadWriteLock.readLock().unlock();
} }
@@ -726,7 +739,7 @@ public final class AppSearchImpl implements Closeable {
} }
} else { } else {
// Client didn't specify certain schemas to search over, check all schemas // Client didn't specify certain schemas to search over, check all schemas
Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix); Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix).keySet();
if (prefixedSchemas != null) { if (prefixedSchemas != null) {
for (String prefixedSchema : prefixedSchemas) { for (String prefixedSchema : prefixedSchemas) {
if (packageName.equals(callerPackageName) if (packageName.equals(callerPackageName)
@@ -816,7 +829,7 @@ public final class AppSearchImpl implements Closeable {
searchSpecBuilder.build(), scoringSpec, resultSpecBuilder.build()); searchSpecBuilder.build(), scoringSpec, resultSpecBuilder.build());
checkSuccess(searchResultProto.getStatus()); checkSuccess(searchResultProto.getStatus());
return rewriteSearchResultProto(searchResultProto); return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
} }
/** /**
@@ -838,7 +851,7 @@ public final class AppSearchImpl implements Closeable {
SearchResultProto searchResultProto = SearchResultProto searchResultProto =
mIcingSearchEngineLocked.getNextPage(nextPageToken); mIcingSearchEngineLocked.getNextPage(nextPageToken);
checkSuccess(searchResultProto.getStatus()); checkSuccess(searchResultProto.getStatus());
return rewriteSearchResultProto(searchResultProto); return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
} finally { } finally {
mReadWriteLock.readLock().unlock(); mReadWriteLock.readLock().unlock();
} }
@@ -1104,22 +1117,31 @@ public final class AppSearchImpl implements Closeable {
/** /**
* Persists all update/delete requests to the disk. * Persists all update/delete requests to the disk.
* *
* <p>If the app crashes after a call to PersistToDisk(), Icing would be able to fully recover * <p>If the app crashes after a call to PersistToDisk with {@link PersistType.Code#FULL}, Icing
* all data written up to this point without a costly recovery process. * would be able to fully recover all data written up to this point without a costly recovery
* process.
* *
* <p>If the app crashes before a call to PersistToDisk(), Icing would trigger a costly recovery * <p>If the app crashes after a call to PersistToDisk with {@link PersistType.Code#LITE}, Icing
* process in next initialization. After that, Icing would still be able to recover all written * would trigger a costly recovery process in next initialization. After that, Icing would still
* data. * be able to recover all written data - excepting Usage data. Usage data is only guaranteed to
* be safe after a call to PersistToDisk with {@link PersistType.Code#FULL}
* *
* <p>If the app crashes after an update/delete request has been made, but before any call to
* PersistToDisk, then all data in Icing will be lost.
*
* @param persistType the amount of data to persist. {@link PersistType.Code#LITE} will only
* persist the minimal amount of data to ensure all data can be recovered. {@link
* PersistType.Code#FULL} will persist all data necessary to prevent data loss without
* needing data recovery.
* @throws AppSearchException on any error that AppSearch persist data to disk. * @throws AppSearchException on any error that AppSearch persist data to disk.
*/ */
public void persistToDisk() throws AppSearchException { public void persistToDisk(@NonNull PersistType.Code persistType) throws AppSearchException {
mReadWriteLock.writeLock().lock(); mReadWriteLock.writeLock().lock();
try { try {
throwIfClosedLocked(); throwIfClosedLocked();
PersistToDiskResultProto persistToDiskResultProto = PersistToDiskResultProto persistToDiskResultProto =
mIcingSearchEngineLocked.persistToDisk(PersistType.Code.FULL); mIcingSearchEngineLocked.persistToDisk(persistType);
checkSuccess(persistToDiskResultProto.getStatus()); checkSuccess(persistToDiskResultProto.getStatus());
} finally { } finally {
mReadWriteLock.writeLock().unlock(); mReadWriteLock.writeLock().unlock();
@@ -1189,8 +1211,8 @@ public final class AppSearchImpl implements Closeable {
// Any prefixed types that used to exist in the schema, but are deleted in the new one. // Any prefixed types that used to exist in the schema, but are deleted in the new one.
final Set<String> mDeletedPrefixedTypes = new ArraySet<>(); final Set<String> mDeletedPrefixedTypes = new ArraySet<>();
// Prefixed types that were part of the new schema. // Map of prefixed schema types to SchemaTypeConfigProtos that were part of the new schema.
final Set<String> mRewrittenPrefixedTypes = new ArraySet<>(); final Map<String, SchemaTypeConfigProto> mRewrittenPrefixedTypes = new ArrayMap<>();
} }
/** /**
@@ -1238,7 +1260,7 @@ public final class AppSearchImpl implements Closeable {
// newTypesToProto is modified below, so we need a copy first // newTypesToProto is modified below, so we need a copy first
RewrittenSchemaResults rewrittenSchemaResults = new RewrittenSchemaResults(); RewrittenSchemaResults rewrittenSchemaResults = new RewrittenSchemaResults();
rewrittenSchemaResults.mRewrittenPrefixedTypes.addAll(newTypesToProto.keySet()); rewrittenSchemaResults.mRewrittenPrefixedTypes.putAll(newTypesToProto);
// Combine the existing schema (which may have types from other prefixes) with this // Combine the existing schema (which may have types from other prefixes) with this
// prefix's new schema. Modifies the existingSchemaBuilder. // prefix's new schema. Modifies the existingSchemaBuilder.
@@ -1263,99 +1285,6 @@ public final class AppSearchImpl implements Closeable {
return rewrittenSchemaResults; return rewrittenSchemaResults;
} }
/**
* Prepends {@code prefix} to all types and namespaces mentioned anywhere in {@code
* documentBuilder}.
*
* @param documentBuilder The document to mutate
* @param prefix The prefix to add
*/
@VisibleForTesting
static void addPrefixToDocument(
@NonNull DocumentProto.Builder documentBuilder, @NonNull String prefix) {
// Rewrite the type name to include/remove the prefix.
String newSchema = prefix + documentBuilder.getSchema();
documentBuilder.setSchema(newSchema);
// Rewrite the namespace to include/remove the prefix.
documentBuilder.setNamespace(prefix + documentBuilder.getNamespace());
// Recurse into derived documents
for (int propertyIdx = 0;
propertyIdx < documentBuilder.getPropertiesCount();
propertyIdx++) {
int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount();
if (documentCount > 0) {
PropertyProto.Builder propertyBuilder =
documentBuilder.getProperties(propertyIdx).toBuilder();
for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) {
DocumentProto.Builder derivedDocumentBuilder =
propertyBuilder.getDocumentValues(documentIdx).toBuilder();
addPrefixToDocument(derivedDocumentBuilder, prefix);
propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder);
}
documentBuilder.setProperties(propertyIdx, propertyBuilder);
}
}
}
/**
* Removes any prefixes from types and namespaces mentioned anywhere in {@code documentBuilder}.
*
* @param documentBuilder The document to mutate
* @return Prefix name that was removed from the document.
* @throws AppSearchException if there are unexpected database prefixing errors.
*/
@NonNull
@VisibleForTesting
static String removePrefixesFromDocument(@NonNull DocumentProto.Builder documentBuilder)
throws AppSearchException {
// Rewrite the type name and namespace to remove the prefix.
String schemaPrefix = getPrefix(documentBuilder.getSchema());
String namespacePrefix = getPrefix(documentBuilder.getNamespace());
if (!schemaPrefix.equals(namespacePrefix)) {
throw new AppSearchException(
AppSearchResult.RESULT_INTERNAL_ERROR,
"Found unexpected"
+ " multiple prefix names in document: "
+ schemaPrefix
+ ", "
+ namespacePrefix);
}
documentBuilder.setSchema(removePrefix(documentBuilder.getSchema()));
documentBuilder.setNamespace(removePrefix(documentBuilder.getNamespace()));
// Recurse into derived documents
for (int propertyIdx = 0;
propertyIdx < documentBuilder.getPropertiesCount();
propertyIdx++) {
int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount();
if (documentCount > 0) {
PropertyProto.Builder propertyBuilder =
documentBuilder.getProperties(propertyIdx).toBuilder();
for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) {
DocumentProto.Builder derivedDocumentBuilder =
propertyBuilder.getDocumentValues(documentIdx).toBuilder();
String nestedPrefix = removePrefixesFromDocument(derivedDocumentBuilder);
if (!nestedPrefix.equals(schemaPrefix)) {
throw new AppSearchException(
AppSearchResult.RESULT_INTERNAL_ERROR,
"Found unexpected multiple prefix names in document: "
+ schemaPrefix
+ ", "
+ nestedPrefix);
}
propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder);
}
documentBuilder.setProperties(propertyIdx, propertyBuilder);
}
}
return schemaPrefix;
}
/** /**
* Rewrites the search spec filters with {@code prefixes}. * Rewrites the search spec filters with {@code prefixes}.
* *
@@ -1443,9 +1372,9 @@ public final class AppSearchImpl implements Closeable {
if (allowedPrefixedSchemas.isEmpty()) { if (allowedPrefixedSchemas.isEmpty()) {
// If the client didn't specify any schema filters, search over all of their schemas // If the client didn't specify any schema filters, search over all of their schemas
Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix); Map<String, SchemaTypeConfigProto> prefixedSchemaMap = mSchemaMapLocked.get(prefix);
if (prefixedSchemas != null) { if (prefixedSchemaMap != null) {
allowedPrefixedSchemas.addAll(prefixedSchemas); allowedPrefixedSchemas.addAll(prefixedSchemaMap.keySet());
} }
} }
return allowedPrefixedSchemas; return allowedPrefixedSchemas;
@@ -1656,86 +1585,6 @@ public final class AppSearchImpl implements Closeable {
return mSchemaMapLocked.keySet(); return mSchemaMapLocked.keySet();
} }
@NonNull
static String createPrefix(@NonNull String packageName, @NonNull String databaseName) {
return createPackagePrefix(packageName) + databaseName + DATABASE_DELIMITER;
}
@NonNull
private static String createPackagePrefix(@NonNull String packageName) {
return packageName + PACKAGE_DELIMITER;
}
/**
* Returns the package name that's contained within the {@code prefix}.
*
* @param prefix Prefix string that contains the package name inside of it. The package name
* must be in the front of the string, and separated from the rest of the string by the
* {@link #PACKAGE_DELIMITER}.
* @return Valid package name.
*/
@NonNull
private static String getPackageName(@NonNull String prefix) {
int delimiterIndex = prefix.indexOf(PACKAGE_DELIMITER);
if (delimiterIndex == -1) {
// This should never happen if we construct our prefixes properly
Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix);
return "";
}
return prefix.substring(0, delimiterIndex);
}
/**
* Returns the database name that's contained within the {@code prefix}.
*
* @param prefix Prefix string that contains the database name inside of it. The database name
* must be between the {@link #PACKAGE_DELIMITER} and {@link #DATABASE_DELIMITER}
* @return Valid database name.
*/
@NonNull
private static String getDatabaseName(@NonNull String prefix) {
int packageDelimiterIndex = prefix.indexOf(PACKAGE_DELIMITER);
int databaseDelimiterIndex = prefix.indexOf(DATABASE_DELIMITER);
if (packageDelimiterIndex == -1) {
// This should never happen if we construct our prefixes properly
Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix);
return "";
}
if (databaseDelimiterIndex == -1) {
// This should never happen if we construct our prefixes properly
Log.wtf(TAG, "Malformed prefix doesn't contain database delimiter: " + prefix);
return "";
}
return prefix.substring(packageDelimiterIndex + 1, databaseDelimiterIndex);
}
@NonNull
private static String removePrefix(@NonNull String prefixedString) throws AppSearchException {
// The prefix is made up of the package, then the database. So we only need to find the
// database cutoff.
int delimiterIndex;
if ((delimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER)) != -1) {
// Add 1 to include the char size of the DATABASE_DELIMITER
return prefixedString.substring(delimiterIndex + 1);
}
throw new AppSearchException(
AppSearchResult.RESULT_UNKNOWN_ERROR,
"The prefixed value doesn't contains a valid database name.");
}
@NonNull
private static String getPrefix(@NonNull String prefixedString) throws AppSearchException {
int databaseDelimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER);
if (databaseDelimiterIndex == -1) {
throw new AppSearchException(
AppSearchResult.RESULT_UNKNOWN_ERROR,
"The databaseName prefixed value doesn't contain a valid database name.");
}
// Add 1 to include the char size of the DATABASE_DELIMITER
return prefixedString.substring(0, databaseDelimiterIndex + 1);
}
private static void addToMap( private static void addToMap(
Map<String, Set<String>> map, String prefix, String prefixedValue) { Map<String, Set<String>> map, String prefix, String prefixedValue) {
Set<String> values = map.get(prefix); Set<String> values = map.get(prefix);
@@ -1746,6 +1595,26 @@ public final class AppSearchImpl implements Closeable {
values.add(prefixedValue); values.add(prefixedValue);
} }
private static void addToMap(
Map<String, Map<String, SchemaTypeConfigProto>> map,
String prefix,
SchemaTypeConfigProto schemaTypeConfigProto) {
Map<String, SchemaTypeConfigProto> schemaTypeMap = map.get(prefix);
if (schemaTypeMap == null) {
schemaTypeMap = new ArrayMap<>();
map.put(prefix, schemaTypeMap);
}
schemaTypeMap.put(schemaTypeConfigProto.getSchemaType(), schemaTypeConfigProto);
}
private static void removeFromMap(
Map<String, Map<String, SchemaTypeConfigProto>> map, String prefix, String schemaType) {
Map<String, SchemaTypeConfigProto> schemaTypeMap = map.get(prefix);
if (schemaTypeMap != null) {
schemaTypeMap.remove(schemaType);
}
}
/** /**
* Checks the given status code and throws an {@link AppSearchException} if code is an error. * Checks the given status code and throws an {@link AppSearchException} if code is an error.
* *
@@ -1853,7 +1722,9 @@ public final class AppSearchImpl implements Closeable {
/** Remove the rewritten schema types from any result documents. */ /** Remove the rewritten schema types from any result documents. */
@NonNull @NonNull
@VisibleForTesting @VisibleForTesting
static SearchResultPage rewriteSearchResultProto(@NonNull SearchResultProto searchResultProto) static SearchResultPage rewriteSearchResultProto(
@NonNull SearchResultProto searchResultProto,
@NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap)
throws AppSearchException { throws AppSearchException {
// Parallel array of package names for each document search result. // Parallel array of package names for each document search result.
List<String> packageNames = new ArrayList<>(searchResultProto.getResultsCount()); List<String> packageNames = new ArrayList<>(searchResultProto.getResultsCount());
@@ -1873,7 +1744,7 @@ public final class AppSearchImpl implements Closeable {
resultsBuilder.setResults(i, resultBuilder); resultsBuilder.setResults(i, resultBuilder);
} }
return SearchResultToProtoConverter.toSearchResultPage( return SearchResultToProtoConverter.toSearchResultPage(
resultsBuilder, packageNames, databaseNames); resultsBuilder, packageNames, databaseNames, schemaMap);
} }
@GuardedBy("mReadWriteLock") @GuardedBy("mReadWriteLock")

View File

@@ -18,11 +18,12 @@ package com.android.server.appsearch.external.localstorage;
import android.annotation.NonNull; import android.annotation.NonNull;
import com.android.internal.util.Preconditions;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.google.android.icing.proto.PutDocumentStatsProto; import com.google.android.icing.proto.PutDocumentStatsProto;
import java.util.Objects;
/** /**
* Class contains helper functions for logging. * Class contains helper functions for logging.
* *
@@ -42,8 +43,8 @@ public final class AppSearchLoggerHelper {
static void copyNativeStats( static void copyNativeStats(
@NonNull PutDocumentStatsProto fromNativeStats, @NonNull PutDocumentStatsProto fromNativeStats,
@NonNull PutDocumentStats.Builder toStatsBuilder) { @NonNull PutDocumentStats.Builder toStatsBuilder) {
Preconditions.checkNotNull(fromNativeStats); Objects.requireNonNull(fromNativeStats);
Preconditions.checkNotNull(toStatsBuilder); Objects.requireNonNull(toStatsBuilder);
toStatsBuilder toStatsBuilder
.setNativeLatencyMillis(fromNativeStats.getLatencyMs()) .setNativeLatencyMillis(fromNativeStats.getLatencyMs())
.setNativeDocumentStoreLatencyMillis(fromNativeStats.getDocumentStoreLatencyMs()) .setNativeDocumentStoreLatencyMillis(fromNativeStats.getDocumentStoreLatencyMs())

View File

@@ -17,16 +17,18 @@
package com.android.server.appsearch.external.localstorage.converter; package com.android.server.appsearch.external.localstorage.converter;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.appsearch.AppSearchSchema;
import android.app.appsearch.GenericDocument; import android.app.appsearch.GenericDocument;
import com.android.internal.util.Preconditions;
import com.google.android.icing.proto.DocumentProto; import com.google.android.icing.proto.DocumentProto;
import com.google.android.icing.proto.PropertyProto; import com.google.android.icing.proto.PropertyProto;
import com.google.android.icing.proto.SchemaTypeConfigProto;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Map;
import java.util.Objects;
/** /**
* Translates a {@link GenericDocument} into a {@link DocumentProto}. * Translates a {@link GenericDocument} into a {@link DocumentProto}.
@@ -34,13 +36,20 @@ import java.util.Collections;
* @hide * @hide
*/ */
public final class GenericDocumentToProtoConverter { public final class GenericDocumentToProtoConverter {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final long[] EMPTY_LONG_ARRAY = new long[0];
private static final double[] EMPTY_DOUBLE_ARRAY = new double[0];
private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0];
private static final byte[][] EMPTY_BYTES_ARRAY = new byte[0][0];
private static final GenericDocument[] EMPTY_DOCUMENT_ARRAY = new GenericDocument[0];
private GenericDocumentToProtoConverter() {} private GenericDocumentToProtoConverter() {}
/** Converts a {@link GenericDocument} into a {@link DocumentProto}. */ /** Converts a {@link GenericDocument} into a {@link DocumentProto}. */
@NonNull @NonNull
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static DocumentProto toDocumentProto(@NonNull GenericDocument document) { public static DocumentProto toDocumentProto(@NonNull GenericDocument document) {
Preconditions.checkNotNull(document); Objects.requireNonNull(document);
DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder(); DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder();
mProtoBuilder mProtoBuilder
.setUri(document.getUri()) .setUri(document.getUri())
@@ -97,16 +106,34 @@ public final class GenericDocumentToProtoConverter {
return mProtoBuilder.build(); return mProtoBuilder.build();
} }
/** Converts a {@link DocumentProto} into a {@link GenericDocument}. */ /**
* Converts a {@link DocumentProto} into a {@link GenericDocument}.
*
* <p>In the case that the {@link DocumentProto} object proto has no values set, the converter
* searches for the matching property name in the {@link SchemaTypeConfigProto} object for the
* document, and infers the correct default value to set for the empty property based on the
* data type of the property defined by the schema type.
*
* @param proto the document to convert to a {@link GenericDocument} instance. The document
* proto should have its package + database prefix stripped from its fields.
* @param prefix the package + database prefix used searching the {@code schemaTypeMap}.
* @param schemaTypeMap map of prefixed schema type to {@link SchemaTypeConfigProto}, used for
* looking up the default empty value to set for a document property that has all empty
* values.
*/
@NonNull @NonNull
public static GenericDocument toGenericDocument(@NonNull DocumentProto proto) { public static GenericDocument toGenericDocument(
Preconditions.checkNotNull(proto); @NonNull DocumentProto proto,
@NonNull String prefix,
@NonNull Map<String, SchemaTypeConfigProto> schemaTypeMap) {
Objects.requireNonNull(proto);
GenericDocument.Builder<?> documentBuilder = GenericDocument.Builder<?> documentBuilder =
new GenericDocument.Builder<>( new GenericDocument.Builder<>(
proto.getNamespace(), proto.getUri(), proto.getSchema()) proto.getNamespace(), proto.getUri(), proto.getSchema())
.setScore(proto.getScore()) .setScore(proto.getScore())
.setTtlMillis(proto.getTtlMs()) .setTtlMillis(proto.getTtlMs())
.setCreationTimestampMillis(proto.getCreationTimestampMs()); .setCreationTimestampMillis(proto.getCreationTimestampMs());
String prefixedSchemaType = prefix + proto.getSchema();
for (int i = 0; i < proto.getPropertiesCount(); i++) { for (int i = 0; i < proto.getPropertiesCount(); i++) {
PropertyProto property = proto.getProperties(i); PropertyProto property = proto.getProperties(i);
@@ -144,13 +171,51 @@ public final class GenericDocumentToProtoConverter {
} else if (property.getDocumentValuesCount() > 0) { } else if (property.getDocumentValuesCount() > 0) {
GenericDocument[] values = new GenericDocument[property.getDocumentValuesCount()]; GenericDocument[] values = new GenericDocument[property.getDocumentValuesCount()];
for (int j = 0; j < values.length; j++) { for (int j = 0; j < values.length; j++) {
values[j] = toGenericDocument(property.getDocumentValues(j)); values[j] =
toGenericDocument(property.getDocumentValues(j), prefix, schemaTypeMap);
} }
documentBuilder.setPropertyDocument(name, values); documentBuilder.setPropertyDocument(name, values);
} else { } else {
throw new IllegalStateException("Unknown type of value: " + name); // TODO(b/184966497): Optimize by caching PropertyConfigProto
setEmptyProperty(name, documentBuilder, schemaTypeMap.get(prefixedSchemaType));
} }
} }
return documentBuilder.build(); return documentBuilder.build();
} }
private static void setEmptyProperty(
@NonNull String propertyName,
@NonNull GenericDocument.Builder<?> documentBuilder,
@NonNull SchemaTypeConfigProto schema) {
@AppSearchSchema.PropertyConfig.DataType int dataType = 0;
for (int i = 0; i < schema.getPropertiesCount(); ++i) {
if (propertyName.equals(schema.getProperties(i).getPropertyName())) {
dataType = schema.getProperties(i).getDataType().getNumber();
break;
}
}
switch (dataType) {
case AppSearchSchema.PropertyConfig.DATA_TYPE_STRING:
documentBuilder.setPropertyString(propertyName, EMPTY_STRING_ARRAY);
break;
case AppSearchSchema.PropertyConfig.DATA_TYPE_INT64:
documentBuilder.setPropertyLong(propertyName, EMPTY_LONG_ARRAY);
break;
case AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE:
documentBuilder.setPropertyDouble(propertyName, EMPTY_DOUBLE_ARRAY);
break;
case AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN:
documentBuilder.setPropertyBoolean(propertyName, EMPTY_BOOLEAN_ARRAY);
break;
case AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES:
documentBuilder.setPropertyBytes(propertyName, EMPTY_BYTES_ARRAY);
break;
case AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT:
documentBuilder.setPropertyDocument(propertyName, EMPTY_DOCUMENT_ARRAY);
break;
default:
throw new IllegalStateException("Unknown type of value: " + propertyName);
}
}
} }

View File

@@ -20,8 +20,6 @@ import android.annotation.NonNull;
import android.app.appsearch.AppSearchSchema; import android.app.appsearch.AppSearchSchema;
import android.util.Log; import android.util.Log;
import com.android.internal.util.Preconditions;
import com.google.android.icing.proto.DocumentIndexingConfig; import com.google.android.icing.proto.DocumentIndexingConfig;
import com.google.android.icing.proto.PropertyConfigProto; import com.google.android.icing.proto.PropertyConfigProto;
import com.google.android.icing.proto.SchemaTypeConfigProto; import com.google.android.icing.proto.SchemaTypeConfigProto;
@@ -30,6 +28,7 @@ import com.google.android.icing.proto.StringIndexingConfig;
import com.google.android.icing.proto.TermMatchType; import com.google.android.icing.proto.TermMatchType;
import java.util.List; import java.util.List;
import java.util.Objects;
/** /**
* Translates an {@link AppSearchSchema} into a {@link SchemaTypeConfigProto}. * Translates an {@link AppSearchSchema} into a {@link SchemaTypeConfigProto}.
@@ -48,7 +47,7 @@ public final class SchemaToProtoConverter {
@NonNull @NonNull
public static SchemaTypeConfigProto toSchemaTypeConfigProto( public static SchemaTypeConfigProto toSchemaTypeConfigProto(
@NonNull AppSearchSchema schema, int version) { @NonNull AppSearchSchema schema, int version) {
Preconditions.checkNotNull(schema); Objects.requireNonNull(schema);
SchemaTypeConfigProto.Builder protoBuilder = SchemaTypeConfigProto.Builder protoBuilder =
SchemaTypeConfigProto.newBuilder() SchemaTypeConfigProto.newBuilder()
.setSchemaType(schema.getSchemaType()) .setSchemaType(schema.getSchemaType())
@@ -64,7 +63,7 @@ public final class SchemaToProtoConverter {
@NonNull @NonNull
private static PropertyConfigProto toPropertyConfigProto( private static PropertyConfigProto toPropertyConfigProto(
@NonNull AppSearchSchema.PropertyConfig property) { @NonNull AppSearchSchema.PropertyConfig property) {
Preconditions.checkNotNull(property); Objects.requireNonNull(property);
PropertyConfigProto.Builder builder = PropertyConfigProto.Builder builder =
PropertyConfigProto.newBuilder().setPropertyName(property.getName()); PropertyConfigProto.newBuilder().setPropertyName(property.getName());
@@ -116,7 +115,7 @@ public final class SchemaToProtoConverter {
*/ */
@NonNull @NonNull
public static AppSearchSchema toAppSearchSchema(@NonNull SchemaTypeConfigProtoOrBuilder proto) { public static AppSearchSchema toAppSearchSchema(@NonNull SchemaTypeConfigProtoOrBuilder proto) {
Preconditions.checkNotNull(proto); Objects.requireNonNull(proto);
AppSearchSchema.Builder builder = new AppSearchSchema.Builder(proto.getSchemaType()); AppSearchSchema.Builder builder = new AppSearchSchema.Builder(proto.getSchemaType());
List<PropertyConfigProto> properties = proto.getPropertiesList(); List<PropertyConfigProto> properties = proto.getPropertiesList();
for (int i = 0; i < properties.size(); i++) { for (int i = 0; i < properties.size(); i++) {
@@ -129,7 +128,7 @@ public final class SchemaToProtoConverter {
@NonNull @NonNull
private static AppSearchSchema.PropertyConfig toPropertyConfig( private static AppSearchSchema.PropertyConfig toPropertyConfig(
@NonNull PropertyConfigProto proto) { @NonNull PropertyConfigProto proto) {
Preconditions.checkNotNull(proto); Objects.requireNonNull(proto);
switch (proto.getDataType()) { switch (proto.getDataType()) {
case STRING: case STRING:
return toStringPropertyConfig(proto); return toStringPropertyConfig(proto);

View File

@@ -16,6 +16,8 @@
package com.android.server.appsearch.external.localstorage.converter; package com.android.server.appsearch.external.localstorage.converter;
import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.appsearch.GenericDocument; import android.app.appsearch.GenericDocument;
import android.app.appsearch.SearchResult; import android.app.appsearch.SearchResult;
@@ -24,6 +26,7 @@ import android.os.Bundle;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import com.google.android.icing.proto.SchemaTypeConfigProto;
import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.SearchResultProto;
import com.google.android.icing.proto.SearchResultProtoOrBuilder; import com.google.android.icing.proto.SearchResultProtoOrBuilder;
import com.google.android.icing.proto.SnippetMatchProto; import com.google.android.icing.proto.SnippetMatchProto;
@@ -31,6 +34,7 @@ import com.google.android.icing.proto.SnippetProto;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Translates a {@link SearchResultProto} into {@link SearchResult}s. * Translates a {@link SearchResultProto} into {@link SearchResult}s.
@@ -49,13 +53,17 @@ public class SearchResultToProtoConverter {
* @param databaseNames A parallel array of database names. The database name at index 'i' of * @param databaseNames A parallel array of database names. The database name at index 'i' of
* this list shold be the database that indexed the document at index 'i' of * this list shold be the database that indexed the document at index 'i' of
* proto.getResults(i). * proto.getResults(i).
* @param schemaMap A map of prefixes to an inner-map of prefixed schema type to
* SchemaTypeConfigProtos, used for setting a default value for results with DocumentProtos
* that have empty values.
* @return {@link SearchResultPage} of results. * @return {@link SearchResultPage} of results.
*/ */
@NonNull @NonNull
public static SearchResultPage toSearchResultPage( public static SearchResultPage toSearchResultPage(
@NonNull SearchResultProtoOrBuilder proto, @NonNull SearchResultProtoOrBuilder proto,
@NonNull List<String> packageNames, @NonNull List<String> packageNames,
@NonNull List<String> databaseNames) { @NonNull List<String> databaseNames,
@NonNull Map<String, Map<String, SchemaTypeConfigProto>> schemaMap) {
Preconditions.checkArgument( Preconditions.checkArgument(
proto.getResultsCount() == packageNames.size(), proto.getResultsCount() == packageNames.size(),
"Size of results does not match the number of package names."); "Size of results does not match the number of package names.");
@@ -63,8 +71,14 @@ public class SearchResultToProtoConverter {
bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken()); bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken());
ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount()); ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount());
for (int i = 0; i < proto.getResultsCount(); i++) { for (int i = 0; i < proto.getResultsCount(); i++) {
String prefix = createPrefix(packageNames.get(i), databaseNames.get(i));
Map<String, SchemaTypeConfigProto> schemaTypeMap = schemaMap.get(prefix);
SearchResult result = SearchResult result =
toSearchResult(proto.getResults(i), packageNames.get(i), databaseNames.get(i)); toSearchResult(
proto.getResults(i),
packageNames.get(i),
databaseNames.get(i),
schemaTypeMap);
resultBundles.add(result.getBundle()); resultBundles.add(result.getBundle());
} }
bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles); bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles);
@@ -77,15 +91,21 @@ public class SearchResultToProtoConverter {
* @param proto The proto to be converted. * @param proto The proto to be converted.
* @param packageName The package name associated with the document in {@code proto}. * @param packageName The package name associated with the document in {@code proto}.
* @param databaseName The database name associated with the document in {@code proto}. * @param databaseName The database name associated with the document in {@code proto}.
* @param schemaTypeToProtoMap A map of prefixed schema types to their corresponding
* SchemaTypeConfigProto, used for setting a default value for results with DocumentProtos
* that have empty values.
* @return A {@link SearchResult} bundle. * @return A {@link SearchResult} bundle.
*/ */
@NonNull @NonNull
private static SearchResult toSearchResult( private static SearchResult toSearchResult(
@NonNull SearchResultProto.ResultProtoOrBuilder proto, @NonNull SearchResultProto.ResultProtoOrBuilder proto,
@NonNull String packageName, @NonNull String packageName,
@NonNull String databaseName) { @NonNull String databaseName,
@NonNull Map<String, SchemaTypeConfigProto> schemaTypeToProtoMap) {
String prefix = createPrefix(packageName, databaseName);
GenericDocument document = GenericDocument document =
GenericDocumentToProtoConverter.toGenericDocument(proto.getDocument()); GenericDocumentToProtoConverter.toGenericDocument(
proto.getDocument(), prefix, schemaTypeToProtoMap);
SearchResult.Builder builder = SearchResult.Builder builder =
new SearchResult.Builder(packageName, databaseName) new SearchResult.Builder(packageName, databaseName)
.setGenericDocument(document) .setGenericDocument(document)

View File

@@ -19,13 +19,13 @@ package com.android.server.appsearch.external.localstorage.converter;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.appsearch.SearchSpec; import android.app.appsearch.SearchSpec;
import com.android.internal.util.Preconditions;
import com.google.android.icing.proto.ResultSpecProto; import com.google.android.icing.proto.ResultSpecProto;
import com.google.android.icing.proto.ScoringSpecProto; import com.google.android.icing.proto.ScoringSpecProto;
import com.google.android.icing.proto.SearchSpecProto; import com.google.android.icing.proto.SearchSpecProto;
import com.google.android.icing.proto.TermMatchType; import com.google.android.icing.proto.TermMatchType;
import java.util.Objects;
/** /**
* Translates a {@link SearchSpec} into icing search protos. * Translates a {@link SearchSpec} into icing search protos.
* *
@@ -37,7 +37,7 @@ public final class SearchSpecToProtoConverter {
/** Extracts {@link SearchSpecProto} information from a {@link SearchSpec}. */ /** Extracts {@link SearchSpecProto} information from a {@link SearchSpec}. */
@NonNull @NonNull
public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) { public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) {
Preconditions.checkNotNull(spec); Objects.requireNonNull(spec);
SearchSpecProto.Builder protoBuilder = SearchSpecProto.Builder protoBuilder =
SearchSpecProto.newBuilder() SearchSpecProto.newBuilder()
.addAllSchemaTypeFilters(spec.getFilterSchemas()) .addAllSchemaTypeFilters(spec.getFilterSchemas())
@@ -56,7 +56,7 @@ public final class SearchSpecToProtoConverter {
/** Extracts {@link ResultSpecProto} information from a {@link SearchSpec}. */ /** Extracts {@link ResultSpecProto} information from a {@link SearchSpec}. */
@NonNull @NonNull
public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) { public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) {
Preconditions.checkNotNull(spec); Objects.requireNonNull(spec);
return ResultSpecProto.newBuilder() return ResultSpecProto.newBuilder()
.setNumPerPage(spec.getResultCountPerPage()) .setNumPerPage(spec.getResultCountPerPage())
.setSnippetSpec( .setSnippetSpec(
@@ -73,7 +73,7 @@ public final class SearchSpecToProtoConverter {
/** Extracts {@link ScoringSpecProto} information from a {@link SearchSpec}. */ /** Extracts {@link ScoringSpecProto} information from a {@link SearchSpec}. */
@NonNull @NonNull
public static ScoringSpecProto toScoringSpecProto(@NonNull SearchSpec spec) { public static ScoringSpecProto toScoringSpecProto(@NonNull SearchSpec spec) {
Preconditions.checkNotNull(spec); Objects.requireNonNull(spec);
ScoringSpecProto.Builder protoBuilder = ScoringSpecProto.newBuilder(); ScoringSpecProto.Builder protoBuilder = ScoringSpecProto.newBuilder();
@SearchSpec.Order int orderCode = spec.getOrder(); @SearchSpec.Order int orderCode = spec.getOrder();

View File

@@ -19,10 +19,10 @@ package com.android.server.appsearch.external.localstorage.converter;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.appsearch.SetSchemaResponse; import android.app.appsearch.SetSchemaResponse;
import com.android.internal.util.Preconditions;
import com.google.android.icing.proto.SetSchemaResultProto; import com.google.android.icing.proto.SetSchemaResultProto;
import java.util.Objects;
/** /**
* Translates a {@link SetSchemaResultProto} into {@link SetSchemaResponse}. * Translates a {@link SetSchemaResultProto} into {@link SetSchemaResponse}.
* *
@@ -42,8 +42,8 @@ public class SetSchemaResponseToProtoConverter {
@NonNull @NonNull
public static SetSchemaResponse toSetSchemaResponse( public static SetSchemaResponse toSetSchemaResponse(
@NonNull SetSchemaResultProto proto, @NonNull String prefix) { @NonNull SetSchemaResultProto proto, @NonNull String prefix) {
Preconditions.checkNotNull(proto); Objects.requireNonNull(proto);
Preconditions.checkNotNull(prefix); Objects.requireNonNull(prefix);
SetSchemaResponse.Builder builder = new SetSchemaResponse.Builder(); SetSchemaResponse.Builder builder = new SetSchemaResponse.Builder();
for (int i = 0; i < proto.getDeletedSchemaTypesCount(); i++) { for (int i = 0; i < proto.getDeletedSchemaTypesCount(); i++) {

View File

@@ -18,13 +18,12 @@ package com.android.server.appsearch.external.localstorage.converter;
import android.annotation.NonNull; import android.annotation.NonNull;
import com.android.internal.util.Preconditions;
import com.google.android.icing.proto.TypePropertyMask; import com.google.android.icing.proto.TypePropertyMask;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
/** /**
* Translates a <code>Map<String, List<String>></code> into <code>List<TypePropertyMask></code>. * Translates a <code>Map<String, List<String>></code> into <code>List<TypePropertyMask></code>.
@@ -38,7 +37,7 @@ public final class TypePropertyPathToProtoConverter {
@NonNull @NonNull
public static List<TypePropertyMask> toTypePropertyMaskList( public static List<TypePropertyMask> toTypePropertyMaskList(
@NonNull Map<String, List<String>> typePropertyPaths) { @NonNull Map<String, List<String>> typePropertyPaths) {
Preconditions.checkNotNull(typePropertyPaths); Objects.requireNonNull(typePropertyPaths);
List<TypePropertyMask> typePropertyMasks = new ArrayList<>(typePropertyPaths.size()); List<TypePropertyMask> typePropertyMasks = new ArrayList<>(typePropertyPaths.size());
for (Map.Entry<String, List<String>> e : typePropertyPaths.entrySet()) { for (Map.Entry<String, List<String>> e : typePropertyPaths.entrySet()) {
typePropertyMasks.add( typePropertyMasks.add(

View File

@@ -19,10 +19,9 @@ package com.android.server.appsearch.external.localstorage.stats;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull; import android.annotation.NonNull;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/** /**
* A class for setting basic information to log for all function calls. * A class for setting basic information to log for all function calls.
@@ -75,8 +74,8 @@ public class CallStats {
private final int mNumOperationsFailed; private final int mNumOperationsFailed;
CallStats(@NonNull Builder builder) { CallStats(@NonNull Builder builder) {
Preconditions.checkNotNull(builder); Objects.requireNonNull(builder);
mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStatsBuilder).build(); mGeneralStats = Objects.requireNonNull(builder.mGeneralStatsBuilder).build();
mCallType = builder.mCallType; mCallType = builder.mCallType;
mEstimatedBinderLatencyMillis = builder.mEstimatedBinderLatencyMillis; mEstimatedBinderLatencyMillis = builder.mEstimatedBinderLatencyMillis;
mNumOperationsSucceeded = builder.mNumOperationsSucceeded; mNumOperationsSucceeded = builder.mNumOperationsSucceeded;
@@ -140,8 +139,8 @@ public class CallStats {
/** Builder takes {@link GeneralStats.Builder}. */ /** Builder takes {@link GeneralStats.Builder}. */
public Builder(@NonNull String packageName, @NonNull String database) { public Builder(@NonNull String packageName, @NonNull String database) {
Preconditions.checkNotNull(packageName); Objects.requireNonNull(packageName);
Preconditions.checkNotNull(database); Objects.requireNonNull(database);
mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database); mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database);
} }

View File

@@ -19,7 +19,7 @@ package com.android.server.appsearch.external.localstorage.stats;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.app.appsearch.AppSearchResult; import android.app.appsearch.AppSearchResult;
import com.android.internal.util.Preconditions; import java.util.Objects;
/** /**
* A class for holding general logging information. * A class for holding general logging information.
@@ -48,9 +48,9 @@ public final class GeneralStats {
private final int mTotalLatencyMillis; private final int mTotalLatencyMillis;
GeneralStats(@NonNull Builder builder) { GeneralStats(@NonNull Builder builder) {
Preconditions.checkNotNull(builder); Objects.requireNonNull(builder);
mPackageName = Preconditions.checkNotNull(builder.mPackageName); mPackageName = Objects.requireNonNull(builder.mPackageName);
mDatabase = Preconditions.checkNotNull(builder.mDatabase); mDatabase = Objects.requireNonNull(builder.mDatabase);
mStatusCode = builder.mStatusCode; mStatusCode = builder.mStatusCode;
mTotalLatencyMillis = builder.mTotalLatencyMillis; mTotalLatencyMillis = builder.mTotalLatencyMillis;
} }
@@ -92,8 +92,8 @@ public final class GeneralStats {
* @param database name of the database logging stats * @param database name of the database logging stats
*/ */
public Builder(@NonNull String packageName, @NonNull String database) { public Builder(@NonNull String packageName, @NonNull String database) {
mPackageName = Preconditions.checkNotNull(packageName); mPackageName = Objects.requireNonNull(packageName);
mDatabase = Preconditions.checkNotNull(database); mDatabase = Objects.requireNonNull(database);
} }
/** Sets status code returned from {@link AppSearchResult#getResultCode()} */ /** Sets status code returned from {@link AppSearchResult#getResultCode()} */

View File

@@ -18,7 +18,7 @@ package com.android.server.appsearch.external.localstorage.stats;
import android.annotation.NonNull; import android.annotation.NonNull;
import com.android.internal.util.Preconditions; import java.util.Objects;
/** /**
* A class for holding detailed stats to log for each individual document put by a {@link * A class for holding detailed stats to log for each individual document put by a {@link
@@ -60,8 +60,8 @@ public final class PutDocumentStats {
private final boolean mNativeExceededMaxNumTokens; private final boolean mNativeExceededMaxNumTokens;
PutDocumentStats(@NonNull Builder builder) { PutDocumentStats(@NonNull Builder builder) {
Preconditions.checkNotNull(builder); Objects.requireNonNull(builder);
mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStatsBuilder).build(); mGeneralStats = Objects.requireNonNull(builder.mGeneralStatsBuilder).build();
mGenerateDocumentProtoLatencyMillis = builder.mGenerateDocumentProtoLatencyMillis; mGenerateDocumentProtoLatencyMillis = builder.mGenerateDocumentProtoLatencyMillis;
mRewriteDocumentTypesLatencyMillis = builder.mRewriteDocumentTypesLatencyMillis; mRewriteDocumentTypesLatencyMillis = builder.mRewriteDocumentTypesLatencyMillis;
mNativeLatencyMillis = builder.mNativeLatencyMillis; mNativeLatencyMillis = builder.mNativeLatencyMillis;
@@ -142,8 +142,8 @@ public final class PutDocumentStats {
/** Builder takes {@link GeneralStats.Builder}. */ /** Builder takes {@link GeneralStats.Builder}. */
public Builder(@NonNull String packageName, @NonNull String database) { public Builder(@NonNull String packageName, @NonNull String database) {
Preconditions.checkNotNull(packageName); Objects.requireNonNull(packageName);
Preconditions.checkNotNull(database); Objects.requireNonNull(database);
mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database); mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database);
} }

View File

@@ -0,0 +1,229 @@
/*
* Copyright 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.appsearch.external.localstorage.util;
import android.annotation.NonNull;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.exceptions.AppSearchException;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.google.android.icing.proto.DocumentProto;
import com.google.android.icing.proto.PropertyProto;
/**
* Provides utility functions for working with package + database prefixes.
*
* @hide
*/
public class PrefixUtil {
private static final String TAG = "AppSearchPrefixUtil";
@VisibleForTesting public static final char DATABASE_DELIMITER = '/';
@VisibleForTesting public static final char PACKAGE_DELIMITER = '$';
private PrefixUtil() {}
/** Creates prefix string for given package name and database name. */
@NonNull
public static String createPrefix(@NonNull String packageName, @NonNull String databaseName) {
return packageName + PACKAGE_DELIMITER + databaseName + DATABASE_DELIMITER;
}
/** Creates prefix string for given package name. */
@NonNull
public static String createPackagePrefix(@NonNull String packageName) {
return packageName + PACKAGE_DELIMITER;
}
/**
* Returns the package name that's contained within the {@code prefix}.
*
* @param prefix Prefix string that contains the package name inside of it. The package name
* must be in the front of the string, and separated from the rest of the string by the
* {@link #PACKAGE_DELIMITER}.
* @return Valid package name.
*/
@NonNull
public static String getPackageName(@NonNull String prefix) {
int delimiterIndex = prefix.indexOf(PACKAGE_DELIMITER);
if (delimiterIndex == -1) {
// This should never happen if we construct our prefixes properly
Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix);
return "";
}
return prefix.substring(0, delimiterIndex);
}
/**
* Returns the database name that's contained within the {@code prefix}.
*
* @param prefix Prefix string that contains the database name inside of it. The database name
* must be between the {@link #PACKAGE_DELIMITER} and {@link #DATABASE_DELIMITER}
* @return Valid database name.
*/
@NonNull
public static String getDatabaseName(@NonNull String prefix) {
// TODO (b/184050178) Start database delimiter index search from after package delimiter
int packageDelimiterIndex = prefix.indexOf(PACKAGE_DELIMITER);
int databaseDelimiterIndex = prefix.indexOf(DATABASE_DELIMITER);
if (packageDelimiterIndex == -1) {
// This should never happen if we construct our prefixes properly
Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix);
return "";
}
if (databaseDelimiterIndex == -1) {
// This should never happen if we construct our prefixes properly
Log.wtf(TAG, "Malformed prefix doesn't contain database delimiter: " + prefix);
return "";
}
return prefix.substring(packageDelimiterIndex + 1, databaseDelimiterIndex);
}
/**
* Creates a string with the package and database prefix removed from the input string.
*
* @param prefixedString a string containing a package and database prefix.
* @return a string with the package and database prefix removed.
* @throws AppSearchException if the prefixed value does not contain a valid database name.
*/
@NonNull
public static String removePrefix(@NonNull String prefixedString) throws AppSearchException {
// The prefix is made up of the package, then the database. So we only need to find the
// database cutoff.
int delimiterIndex;
if ((delimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER)) != -1) {
// Add 1 to include the char size of the DATABASE_DELIMITER
return prefixedString.substring(delimiterIndex + 1);
}
throw new AppSearchException(
AppSearchResult.RESULT_UNKNOWN_ERROR,
"The prefixed value doesn't contains a valid database name.");
}
/**
* Creates a package and database prefix string from the input string.
*
* @param prefixedString a string containing a package and database prefix.
* @return a string with the package and database prefix
* @throws AppSearchException if the prefixed value does not contain a valid database name.
*/
@NonNull
public static String getPrefix(@NonNull String prefixedString) throws AppSearchException {
int databaseDelimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER);
if (databaseDelimiterIndex == -1) {
throw new AppSearchException(
AppSearchResult.RESULT_UNKNOWN_ERROR,
"The databaseName prefixed value doesn't contain a valid database name.");
}
// Add 1 to include the char size of the DATABASE_DELIMITER
return prefixedString.substring(0, databaseDelimiterIndex + 1);
}
/**
* Prepends {@code prefix} to all types and namespaces mentioned anywhere in {@code
* documentBuilder}.
*
* @param documentBuilder The document to mutate
* @param prefix The prefix to add
*/
public static void addPrefixToDocument(
@NonNull DocumentProto.Builder documentBuilder, @NonNull String prefix) {
// Rewrite the type name to include/remove the prefix.
String newSchema = prefix + documentBuilder.getSchema();
documentBuilder.setSchema(newSchema);
// Rewrite the namespace to include/remove the prefix.
documentBuilder.setNamespace(prefix + documentBuilder.getNamespace());
// Recurse into derived documents
for (int propertyIdx = 0;
propertyIdx < documentBuilder.getPropertiesCount();
propertyIdx++) {
int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount();
if (documentCount > 0) {
PropertyProto.Builder propertyBuilder =
documentBuilder.getProperties(propertyIdx).toBuilder();
for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) {
DocumentProto.Builder derivedDocumentBuilder =
propertyBuilder.getDocumentValues(documentIdx).toBuilder();
addPrefixToDocument(derivedDocumentBuilder, prefix);
propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder);
}
documentBuilder.setProperties(propertyIdx, propertyBuilder);
}
}
}
/**
* Removes any prefixes from types and namespaces mentioned anywhere in {@code documentBuilder}.
*
* @param documentBuilder The document to mutate
* @return Prefix name that was removed from the document.
* @throws AppSearchException if there are unexpected database prefixing errors.
*/
@NonNull
public static String removePrefixesFromDocument(@NonNull DocumentProto.Builder documentBuilder)
throws AppSearchException {
// Rewrite the type name and namespace to remove the prefix.
String schemaPrefix = getPrefix(documentBuilder.getSchema());
String namespacePrefix = getPrefix(documentBuilder.getNamespace());
if (!schemaPrefix.equals(namespacePrefix)) {
throw new AppSearchException(
AppSearchResult.RESULT_INTERNAL_ERROR,
"Found unexpected"
+ " multiple prefix names in document: "
+ schemaPrefix
+ ", "
+ namespacePrefix);
}
documentBuilder.setSchema(removePrefix(documentBuilder.getSchema()));
documentBuilder.setNamespace(removePrefix(documentBuilder.getNamespace()));
// Recurse into derived documents
for (int propertyIdx = 0;
propertyIdx < documentBuilder.getPropertiesCount();
propertyIdx++) {
int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount();
if (documentCount > 0) {
PropertyProto.Builder propertyBuilder =
documentBuilder.getProperties(propertyIdx).toBuilder();
for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) {
DocumentProto.Builder derivedDocumentBuilder =
propertyBuilder.getDocumentValues(documentIdx).toBuilder();
String nestedPrefix = removePrefixesFromDocument(derivedDocumentBuilder);
if (!nestedPrefix.equals(schemaPrefix)) {
throw new AppSearchException(
AppSearchResult.RESULT_INTERNAL_ERROR,
"Found unexpected multiple prefix names in document: "
+ schemaPrefix
+ ", "
+ nestedPrefix);
}
propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder);
}
documentBuilder.setProperties(propertyIdx, propertyBuilder);
}
}
return schemaPrefix;
}
}

View File

@@ -1 +1 @@
I925ec12f4901c7759976c344ba3428210aada8ad If9d1d770d2327d7d0db7d82acfc54787b5de64bc

View File

@@ -187,9 +187,8 @@ public interface AppSearchSessionShim extends Closeable {
* <p>Removed documents will no longer be surfaced by {@link #search} or {@link #getByUri} * <p>Removed documents will no longer be surfaced by {@link #search} or {@link #getByUri}
* calls. * calls.
* *
* <p><b>NOTE:</b>By default, documents are removed via a soft delete operation. Once the * <p>Once the database crosses the document count or byte usage threshold, removed documents
* document crosses the count threshold or byte usage threshold, the documents will be removed * will be deleted from disk.
* from disk.
* *
* @param request {@link RemoveByUriRequest} with URIs and namespace to remove from the index. * @param request {@link RemoveByUriRequest} with URIs and namespace to remove from the index.
* @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The * @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.app.appsearch;
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.expectThrows;
import org.junit.Test;
public class AppSearchResultTest {
@Test
public void testMapNullPointerException() {
NullPointerException e =
expectThrows(
NullPointerException.class,
() -> {
Object o = null;
o.toString();
});
AppSearchResult<?> result = AppSearchResult.throwableToFailedResult(e);
assertThat(result.getResultCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR);
// Makes sure the exception name is included in the string. Some exceptions have terse or
// missing strings so it's confusing to read the output without the exception name.
assertThat(result.getErrorMessage()).startsWith("NullPointerException");
}
}

View File

@@ -32,6 +32,8 @@ import android.content.pm.PackageManager;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@@ -203,7 +205,7 @@ public class AppSearchImplPlatformTest {
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo); mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
// Set schema1 // Set schema1
String prefix = AppSearchImpl.createPrefix("package", "database"); String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema( mAppSearchImpl.setSchema(
"package", "package",
"database", "database",
@@ -280,7 +282,7 @@ public class AppSearchImplPlatformTest {
mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo); mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo); mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
String prefix = AppSearchImpl.createPrefix("package", "database"); String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema( mAppSearchImpl.setSchema(
"package", "package",
"database", "database",
@@ -353,7 +355,7 @@ public class AppSearchImplPlatformTest {
@Test @Test
public void testSetSchema_defaultPlatformVisible() throws Exception { public void testSetSchema_defaultPlatformVisible() throws Exception {
String prefix = AppSearchImpl.createPrefix("package", "database"); String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema( mAppSearchImpl.setSchema(
"package", "package",
"database", "database",
@@ -372,7 +374,7 @@ public class AppSearchImplPlatformTest {
@Test @Test
public void testSetSchema_platformHidden() throws Exception { public void testSetSchema_platformHidden() throws Exception {
String prefix = AppSearchImpl.createPrefix("package", "database"); String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema( mAppSearchImpl.setSchema(
"package", "package",
"database", "database",
@@ -391,7 +393,7 @@ public class AppSearchImplPlatformTest {
@Test @Test
public void testSetSchema_defaultNotPackageAccessible() throws Exception { public void testSetSchema_defaultNotPackageAccessible() throws Exception {
String prefix = AppSearchImpl.createPrefix("package", "database"); String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema( mAppSearchImpl.setSchema(
"package", "package",
"database", "database",
@@ -419,7 +421,7 @@ public class AppSearchImplPlatformTest {
mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo); mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo); mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
String prefix = AppSearchImpl.createPrefix("package", "database"); String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema( mAppSearchImpl.setSchema(
"package", "package",
"database", "database",

View File

@@ -28,6 +28,8 @@ import android.content.pm.PackageManager;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
@@ -78,13 +80,9 @@ public class VisibilityStoreTest {
@Test @Test
public void testValidPackageName() { public void testValidPackageName() {
assertThat(VisibilityStore.PACKAGE_NAME) assertThat(VisibilityStore.PACKAGE_NAME)
.doesNotContain( .doesNotContain(String.valueOf(PrefixUtil.PACKAGE_DELIMITER));
"" + AppSearchImpl.PACKAGE_DELIMITER); // Convert the chars to CharSequences
assertThat(VisibilityStore.PACKAGE_NAME) assertThat(VisibilityStore.PACKAGE_NAME)
.doesNotContain( .doesNotContain(String.valueOf(PrefixUtil.DATABASE_DELIMITER));
""
+ AppSearchImpl
.DATABASE_DELIMITER); // Convert the chars to CharSequences
} }
/** /**
@@ -93,13 +91,9 @@ public class VisibilityStoreTest {
@Test @Test
public void testValidDatabaseName() { public void testValidDatabaseName() {
assertThat(VisibilityStore.DATABASE_NAME) assertThat(VisibilityStore.DATABASE_NAME)
.doesNotContain( .doesNotContain(String.valueOf(PrefixUtil.PACKAGE_DELIMITER));
"" + AppSearchImpl.PACKAGE_DELIMITER); // Convert the chars to CharSequences
assertThat(VisibilityStore.DATABASE_NAME) assertThat(VisibilityStore.DATABASE_NAME)
.doesNotContain( .doesNotContain(String.valueOf(PrefixUtil.DATABASE_DELIMITER));
""
+ AppSearchImpl
.DATABASE_DELIMITER); // Convert the chars to CharSequences
} }
@Test @Test

View File

@@ -16,6 +16,10 @@
package com.android.server.appsearch.external.localstorage; package com.android.server.appsearch.external.localstorage;
import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.addPrefixToDocument;
import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix;
import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.removePrefixesFromDocument;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.expectThrows; import static org.testng.Assert.expectThrows;
@@ -35,8 +39,10 @@ import android.util.ArraySet;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter; import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
import com.android.server.appsearch.proto.DocumentProto; import com.android.server.appsearch.proto.DocumentProto;
import com.android.server.appsearch.proto.GetOptimizeInfoResultProto; import com.android.server.appsearch.proto.GetOptimizeInfoResultProto;
import com.android.server.appsearch.proto.PersistType;
import com.android.server.appsearch.proto.PropertyConfigProto; import com.android.server.appsearch.proto.PropertyConfigProto;
import com.android.server.appsearch.proto.PropertyProto; import com.android.server.appsearch.proto.PropertyProto;
import com.android.server.appsearch.proto.SchemaProto; import com.android.server.appsearch.proto.SchemaProto;
@@ -47,6 +53,7 @@ import com.android.server.appsearch.proto.StringIndexingConfig;
import com.android.server.appsearch.proto.TermMatchType; import com.android.server.appsearch.proto.TermMatchType;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import org.junit.Before; import org.junit.Before;
@@ -54,6 +61,7 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -96,56 +104,71 @@ public class AppSearchImplTest {
// Create a copy so we can modify it. // Create a copy so we can modify it.
List<SchemaTypeConfigProto> existingTypes = List<SchemaTypeConfigProto> existingTypes =
new ArrayList<>(existingSchemaBuilder.getTypesList()); new ArrayList<>(existingSchemaBuilder.getTypesList());
SchemaTypeConfigProto schemaTypeConfigProto1 =
SchemaProto newSchema = SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build();
SchemaProto.newBuilder() SchemaTypeConfigProto schemaTypeConfigProto2 =
.addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build()) SchemaTypeConfigProto.newBuilder()
.addTypes( .setSchemaType("TestType")
SchemaTypeConfigProto.newBuilder() .addProperties(
.setSchemaType("TestType") PropertyConfigProto.newBuilder()
.addProperties( .setPropertyName("subject")
PropertyConfigProto.newBuilder() .setDataType(PropertyConfigProto.DataType.Code.STRING)
.setPropertyName("subject") .setCardinality(
.setDataType( PropertyConfigProto.Cardinality.Code.OPTIONAL)
PropertyConfigProto.DataType.Code .setStringIndexingConfig(
.STRING) StringIndexingConfig.newBuilder()
.setCardinality( .setTokenizerType(
PropertyConfigProto.Cardinality.Code StringIndexingConfig.TokenizerType
.OPTIONAL) .Code.PLAIN)
.setStringIndexingConfig( .setTermMatchType(TermMatchType.Code.PREFIX)
StringIndexingConfig.newBuilder()
.setTokenizerType(
StringIndexingConfig
.TokenizerType
.Code.PLAIN)
.setTermMatchType(
TermMatchType.Code
.PREFIX)
.build())
.build())
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("link")
.setDataType(
PropertyConfigProto.DataType.Code
.DOCUMENT)
.setCardinality(
PropertyConfigProto.Cardinality.Code
.OPTIONAL)
.setSchemaType("RefType")
.build()) .build())
.build()) .build())
.addProperties(
PropertyConfigProto.newBuilder()
.setPropertyName("link")
.setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
.setCardinality(
PropertyConfigProto.Cardinality.Code.OPTIONAL)
.setSchemaType("RefType")
.build())
.build();
SchemaTypeConfigProto schemaTypeConfigProto3 =
SchemaTypeConfigProto.newBuilder().setSchemaType("RefType").build();
SchemaProto newSchema =
SchemaProto.newBuilder()
.addTypes(schemaTypeConfigProto1)
.addTypes(schemaTypeConfigProto2)
.addTypes(schemaTypeConfigProto3)
.build(); .build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
mAppSearchImpl.rewriteSchema( mAppSearchImpl.rewriteSchema(
AppSearchImpl.createPrefix("package", "newDatabase"), createPrefix("package", "newDatabase"), existingSchemaBuilder, newSchema);
existingSchemaBuilder,
newSchema);
// We rewrote all the new types that were added. And nothing was removed. // We rewrote all the new types that were added. And nothing was removed.
assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes) assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet())
.containsExactly("package$newDatabase/Foo", "package$newDatabase/TestType"); .containsExactly(
"package$newDatabase/Foo",
"package$newDatabase/TestType",
"package$newDatabase/RefType");
assertThat(
rewrittenSchemaResults
.mRewrittenPrefixedTypes
.get("package$newDatabase/Foo")
.getSchemaType())
.isEqualTo("package$newDatabase/Foo");
assertThat(
rewrittenSchemaResults
.mRewrittenPrefixedTypes
.get("package$newDatabase/TestType")
.getSchemaType())
.isEqualTo("package$newDatabase/TestType");
assertThat(
rewrittenSchemaResults
.mRewrittenPrefixedTypes
.get("package$newDatabase/RefType")
.getSchemaType())
.isEqualTo("package$newDatabase/RefType");
assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty(); assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty();
SchemaProto expectedSchema = SchemaProto expectedSchema =
@@ -190,6 +213,10 @@ public class AppSearchImplTest {
"package$newDatabase/RefType") "package$newDatabase/RefType")
.build()) .build())
.build()) .build())
.addTypes(
SchemaTypeConfigProto.newBuilder()
.setSchemaType("package$newDatabase/RefType")
.build())
.build(); .build();
existingTypes.addAll(expectedSchema.getTypesList()); existingTypes.addAll(expectedSchema.getTypesList());
@@ -216,12 +243,12 @@ public class AppSearchImplTest {
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
mAppSearchImpl.rewriteSchema( mAppSearchImpl.rewriteSchema(
AppSearchImpl.createPrefix("package", "existingDatabase"), createPrefix("package", "existingDatabase"),
existingSchemaBuilder, existingSchemaBuilder,
newSchema); newSchema);
// Nothing was removed, but the method did rewrite the type name. // Nothing was removed, but the method did rewrite the type name.
assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes) assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet())
.containsExactly("package$existingDatabase/Foo"); .containsExactly("package$existingDatabase/Foo");
assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty(); assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty();
@@ -251,14 +278,15 @@ public class AppSearchImplTest {
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults =
mAppSearchImpl.rewriteSchema( mAppSearchImpl.rewriteSchema(
AppSearchImpl.createPrefix("package", "existingDatabase"), createPrefix("package", "existingDatabase"),
existingSchemaBuilder, existingSchemaBuilder,
newSchema); newSchema);
// Bar type was rewritten, but Foo ended up being deleted since it wasn't included in the // Bar type was rewritten, but Foo ended up being deleted since it wasn't included in the
// new schema. // new schema.
assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes) assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
.containsExactly("package$existingDatabase/Bar"); .containsKey("package$existingDatabase/Bar");
assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet().size()).isEqualTo(1);
assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes) assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes)
.containsExactly("package$existingDatabase/Foo"); .containsExactly("package$existingDatabase/Foo");
@@ -308,8 +336,7 @@ public class AppSearchImplTest {
.build(); .build();
DocumentProto.Builder actualDocument = documentProto.toBuilder(); DocumentProto.Builder actualDocument = documentProto.toBuilder();
mAppSearchImpl.addPrefixToDocument( addPrefixToDocument(actualDocument, createPrefix("package", "databaseName"));
actualDocument, AppSearchImpl.createPrefix("package", "databaseName"));
assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto); assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto);
} }
@@ -347,8 +374,7 @@ public class AppSearchImplTest {
.build(); .build();
DocumentProto.Builder actualDocument = documentProto.toBuilder(); DocumentProto.Builder actualDocument = documentProto.toBuilder();
assertThat(mAppSearchImpl.removePrefixesFromDocument(actualDocument)) assertThat(removePrefixesFromDocument(actualDocument)).isEqualTo("package$databaseName/");
.isEqualTo("package$databaseName/");
assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto); assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto);
} }
@@ -365,8 +391,7 @@ public class AppSearchImplTest {
DocumentProto.Builder actualDocument = documentProto.toBuilder(); DocumentProto.Builder actualDocument = documentProto.toBuilder();
AppSearchException e = AppSearchException e =
expectThrows( expectThrows(
AppSearchException.class, AppSearchException.class, () -> removePrefixesFromDocument(actualDocument));
() -> mAppSearchImpl.removePrefixesFromDocument(actualDocument));
assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names"); assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names");
} }
@@ -391,8 +416,7 @@ public class AppSearchImplTest {
DocumentProto.Builder actualDocument = documentProto.toBuilder(); DocumentProto.Builder actualDocument = documentProto.toBuilder();
AppSearchException e = AppSearchException e =
expectThrows( expectThrows(
AppSearchException.class, AppSearchException.class, () -> removePrefixesFromDocument(actualDocument));
() -> mAppSearchImpl.removePrefixesFromDocument(actualDocument));
assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names"); assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names");
} }
@@ -484,7 +508,7 @@ public class AppSearchImplTest {
// Rewrite SearchSpec // Rewrite SearchSpec
mAppSearchImpl.rewriteSearchSpecForPrefixesLocked( mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
searchSpecProto, searchSpecProto,
Collections.singleton(AppSearchImpl.createPrefix("package", "database")), Collections.singleton(createPrefix("package", "database")),
ImmutableSet.of("package$database/type")); ImmutableSet.of("package$database/type"));
assertThat(searchSpecProto.getSchemaTypeFiltersList()) assertThat(searchSpecProto.getSchemaTypeFiltersList())
.containsExactly("package$database/type"); .containsExactly("package$database/type");
@@ -531,8 +555,7 @@ public class AppSearchImplTest {
mAppSearchImpl.rewriteSearchSpecForPrefixesLocked( mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
searchSpecProto, searchSpecProto,
ImmutableSet.of( ImmutableSet.of(
AppSearchImpl.createPrefix("package", "database1"), createPrefix("package", "database1"), createPrefix("package", "database2")),
AppSearchImpl.createPrefix("package", "database2")),
ImmutableSet.of( ImmutableSet.of(
"package$database1/typeA", "package$database1/typeB", "package$database1/typeA", "package$database1/typeB",
"package$database2/typeA", "package$database2/typeB")); "package$database2/typeA", "package$database2/typeB"));
@@ -573,8 +596,7 @@ public class AppSearchImplTest {
assertThat( assertThat(
mAppSearchImpl.rewriteSearchSpecForPrefixesLocked( mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
searchSpecProto, searchSpecProto,
Collections.singleton( Collections.singleton(createPrefix("package", "database")),
AppSearchImpl.createPrefix("package", "database")),
/*allowedPrefixedSchemas=*/ Collections.emptySet())) /*allowedPrefixedSchemas=*/ Collections.emptySet()))
.isFalse(); .isFalse();
} }
@@ -1082,7 +1104,7 @@ public class AppSearchImplTest {
// Has database1 // Has database1
Set<String> expectedPrefixes = new ArraySet<>(existingPrefixes); Set<String> expectedPrefixes = new ArraySet<>(existingPrefixes);
expectedPrefixes.add(AppSearchImpl.createPrefix("package", "database1")); expectedPrefixes.add(createPrefix("package", "database1"));
mAppSearchImpl.setSchema( mAppSearchImpl.setSchema(
"package", "package",
"database1", "database1",
@@ -1094,7 +1116,7 @@ public class AppSearchImplTest {
assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactlyElementsIn(expectedPrefixes); assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactlyElementsIn(expectedPrefixes);
// Has both databases // Has both databases
expectedPrefixes.add(AppSearchImpl.createPrefix("package", "database2")); expectedPrefixes.add(createPrefix("package", "database2"));
mAppSearchImpl.setSchema( mAppSearchImpl.setSchema(
"package", "package",
"database2", "database2",
@@ -1110,9 +1132,9 @@ public class AppSearchImplTest {
public void testRewriteSearchResultProto() throws Exception { public void testRewriteSearchResultProto() throws Exception {
final String prefix = final String prefix =
"com.package.foo" "com.package.foo"
+ AppSearchImpl.PACKAGE_DELIMITER + PrefixUtil.PACKAGE_DELIMITER
+ "databaseName" + "databaseName"
+ AppSearchImpl.DATABASE_DELIMITER; + PrefixUtil.DATABASE_DELIMITER;
final String uri = "uri"; final String uri = "uri";
final String namespace = prefix + "namespace"; final String namespace = prefix + "namespace";
final String schemaType = prefix + "schema"; final String schemaType = prefix + "schema";
@@ -1128,18 +1150,22 @@ public class AppSearchImplTest {
SearchResultProto.ResultProto.newBuilder().setDocument(documentProto).build(); SearchResultProto.ResultProto.newBuilder().setDocument(documentProto).build();
SearchResultProto searchResultProto = SearchResultProto searchResultProto =
SearchResultProto.newBuilder().addResults(resultProto).build(); SearchResultProto.newBuilder().addResults(resultProto).build();
SchemaTypeConfigProto schemaTypeConfigProto =
SchemaTypeConfigProto.newBuilder().setSchemaType(schemaType).build();
Map<String, Map<String, SchemaTypeConfigProto>> schemaMap =
ImmutableMap.of(prefix, ImmutableMap.of(schemaType, schemaTypeConfigProto));
DocumentProto.Builder strippedDocumentProto = documentProto.toBuilder(); DocumentProto.Builder strippedDocumentProto = documentProto.toBuilder();
AppSearchImpl.removePrefixesFromDocument(strippedDocumentProto); removePrefixesFromDocument(strippedDocumentProto);
SearchResultPage searchResultPage = SearchResultPage searchResultPage =
AppSearchImpl.rewriteSearchResultProto(searchResultProto); AppSearchImpl.rewriteSearchResultProto(searchResultProto, schemaMap);
for (SearchResult result : searchResultPage.getResults()) { for (SearchResult result : searchResultPage.getResults()) {
assertThat(result.getPackageName()).isEqualTo("com.package.foo"); assertThat(result.getPackageName()).isEqualTo("com.package.foo");
assertThat(result.getDatabaseName()).isEqualTo("databaseName"); assertThat(result.getDatabaseName()).isEqualTo("databaseName");
assertThat(result.getGenericDocument()) assertThat(result.getGenericDocument())
.isEqualTo( .isEqualTo(
GenericDocumentToProtoConverter.toGenericDocument( GenericDocumentToProtoConverter.toGenericDocument(
strippedDocumentProto.build())); strippedDocumentProto.build(), prefix, schemaMap.get(prefix)));
} }
} }
@@ -1609,7 +1635,221 @@ public class AppSearchImplTest {
expectThrows( expectThrows(
IllegalStateException.class, IllegalStateException.class,
() -> { () -> {
appSearchImpl.persistToDisk(); appSearchImpl.persistToDisk(PersistType.Code.FULL);
}); });
} }
@Test
public void testPutPersistsWithLiteFlush() throws Exception {
// Setup the index
Context context = ApplicationProvider.getApplicationContext();
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
AppSearchImpl.create(
appsearchDir,
context,
VisibilityStore.NO_OP_USER_ID,
/*globalQuerierPackage=*/ "");
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
appSearchImpl.setSchema(
"package",
"database",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
// Add a document and persist it.
GenericDocument document =
new GenericDocument.Builder<>("namespace1", "uri1", "type").build();
appSearchImpl.putDocument("package", "database", document, /*logger=*/ null);
appSearchImpl.persistToDisk(PersistType.Code.LITE);
GenericDocument getResult =
appSearchImpl.getDocument(
"package", "database", "namespace1", "uri1", Collections.emptyMap());
assertThat(getResult).isEqualTo(document);
// That document should be visible even from another instance.
AppSearchImpl appSearchImpl2 =
AppSearchImpl.create(
appsearchDir,
context,
VisibilityStore.NO_OP_USER_ID,
/*globalQuerierPackage=*/ "");
getResult =
appSearchImpl2.getDocument(
"package", "database", "namespace1", "uri1", Collections.emptyMap());
assertThat(getResult).isEqualTo(document);
}
@Test
public void testDeletePersistsWithLiteFlush() throws Exception {
// Setup the index
Context context = ApplicationProvider.getApplicationContext();
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
AppSearchImpl.create(
appsearchDir,
context,
VisibilityStore.NO_OP_USER_ID,
/*globalQuerierPackage=*/ "");
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
appSearchImpl.setSchema(
"package",
"database",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
// Add two documents and persist them.
GenericDocument document1 =
new GenericDocument.Builder<>("namespace1", "uri1", "type").build();
appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null);
GenericDocument document2 =
new GenericDocument.Builder<>("namespace1", "uri2", "type").build();
appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null);
appSearchImpl.persistToDisk(PersistType.Code.LITE);
GenericDocument getResult =
appSearchImpl.getDocument(
"package", "database", "namespace1", "uri1", Collections.emptyMap());
assertThat(getResult).isEqualTo(document1);
getResult =
appSearchImpl.getDocument(
"package", "database", "namespace1", "uri2", Collections.emptyMap());
assertThat(getResult).isEqualTo(document2);
// Delete the first document
appSearchImpl.remove("package", "database", "namespace1", "uri1");
appSearchImpl.persistToDisk(PersistType.Code.LITE);
expectThrows(
AppSearchException.class,
() ->
appSearchImpl.getDocument(
"package",
"database",
"namespace1",
"uri1",
Collections.emptyMap()));
getResult =
appSearchImpl.getDocument(
"package", "database", "namespace1", "uri2", Collections.emptyMap());
assertThat(getResult).isEqualTo(document2);
// Only the second document should be retrievable from another instance.
AppSearchImpl appSearchImpl2 =
AppSearchImpl.create(
appsearchDir,
context,
VisibilityStore.NO_OP_USER_ID,
/*globalQuerierPackage=*/ "");
expectThrows(
AppSearchException.class,
() ->
appSearchImpl2.getDocument(
"package",
"database",
"namespace1",
"uri1",
Collections.emptyMap()));
getResult =
appSearchImpl2.getDocument(
"package", "database", "namespace1", "uri2", Collections.emptyMap());
assertThat(getResult).isEqualTo(document2);
}
@Test
public void testDeleteByQueryPersistsWithLiteFlush() throws Exception {
// Setup the index
Context context = ApplicationProvider.getApplicationContext();
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
AppSearchImpl.create(
appsearchDir,
context,
VisibilityStore.NO_OP_USER_ID,
/*globalQuerierPackage=*/ "");
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
appSearchImpl.setSchema(
"package",
"database",
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
// Add two documents and persist them.
GenericDocument document1 =
new GenericDocument.Builder<>("namespace1", "uri1", "type").build();
appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null);
GenericDocument document2 =
new GenericDocument.Builder<>("namespace2", "uri2", "type").build();
appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null);
appSearchImpl.persistToDisk(PersistType.Code.LITE);
GenericDocument getResult =
appSearchImpl.getDocument(
"package", "database", "namespace1", "uri1", Collections.emptyMap());
assertThat(getResult).isEqualTo(document1);
getResult =
appSearchImpl.getDocument(
"package", "database", "namespace2", "uri2", Collections.emptyMap());
assertThat(getResult).isEqualTo(document2);
// Delete the first document
appSearchImpl.removeByQuery(
"package",
"database",
"",
new SearchSpec.Builder()
.addFilterNamespaces("namespace1")
.setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
.build());
appSearchImpl.persistToDisk(PersistType.Code.LITE);
expectThrows(
AppSearchException.class,
() ->
appSearchImpl.getDocument(
"package",
"database",
"namespace1",
"uri1",
Collections.emptyMap()));
getResult =
appSearchImpl.getDocument(
"package", "database", "namespace2", "uri2", Collections.emptyMap());
assertThat(getResult).isEqualTo(document2);
// Only the second document should be retrievable from another instance.
AppSearchImpl appSearchImpl2 =
AppSearchImpl.create(
appsearchDir,
context,
VisibilityStore.NO_OP_USER_ID,
/*globalQuerierPackage=*/ "");
expectThrows(
AppSearchException.class,
() ->
appSearchImpl2.getDocument(
"package",
"database",
"namespace1",
"uri1",
Collections.emptyMap()));
getResult =
appSearchImpl2.getDocument(
"package", "database", "namespace2", "uri2", Collections.emptyMap());
assertThat(getResult).isEqualTo(document2);
}
} }

View File

@@ -21,35 +21,50 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.appsearch.GenericDocument; import android.app.appsearch.GenericDocument;
import com.android.server.appsearch.proto.DocumentProto; import com.android.server.appsearch.proto.DocumentProto;
import com.android.server.appsearch.proto.PropertyConfigProto;
import com.android.server.appsearch.proto.PropertyProto; import com.android.server.appsearch.proto.PropertyProto;
import com.android.server.appsearch.proto.SchemaTypeConfigProto;
import com.android.server.appsearch.protobuf.ByteString; import com.android.server.appsearch.protobuf.ByteString;
import com.google.common.collect.ImmutableMap;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
public class GenericDocumentToProtoConverterTest { public class GenericDocumentToProtoConverterTest {
private static final byte[] BYTE_ARRAY_1 = new byte[] {(byte) 1, (byte) 2, (byte) 3}; private static final byte[] BYTE_ARRAY_1 = new byte[] {(byte) 1, (byte) 2, (byte) 3};
private static final byte[] BYTE_ARRAY_2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7}; private static final byte[] BYTE_ARRAY_2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7};
private static final String SCHEMA_TYPE_1 = "sDocumentPropertiesSchemaType1";
private static final String SCHEMA_TYPE_2 = "sDocumentPropertiesSchemaType2";
private static final GenericDocument DOCUMENT_PROPERTIES_1 = private static final GenericDocument DOCUMENT_PROPERTIES_1 =
new GenericDocument.Builder<GenericDocument.Builder<?>>( new GenericDocument.Builder<GenericDocument.Builder<?>>(
"namespace", "sDocumentProperties1", "sDocumentPropertiesSchemaType1") "namespace", "sDocumentProperties1", SCHEMA_TYPE_1)
.setCreationTimestampMillis(12345L) .setCreationTimestampMillis(12345L)
.build(); .build();
private static final GenericDocument DOCUMENT_PROPERTIES_2 = private static final GenericDocument DOCUMENT_PROPERTIES_2 =
new GenericDocument.Builder<GenericDocument.Builder<?>>( new GenericDocument.Builder<GenericDocument.Builder<?>>(
"namespace", "sDocumentProperties2", "sDocumentPropertiesSchemaType2") "namespace", "sDocumentProperties2", SCHEMA_TYPE_2)
.setCreationTimestampMillis(6789L) .setCreationTimestampMillis(6789L)
.build(); .build();
private static final SchemaTypeConfigProto SCHEMA_PROTO_1 =
SchemaTypeConfigProto.newBuilder().setSchemaType(SCHEMA_TYPE_1).build();
private static final SchemaTypeConfigProto SCHEMA_PROTO_2 =
SchemaTypeConfigProto.newBuilder().setSchemaType(SCHEMA_TYPE_2).build();
private static final String PREFIX = "package$databaseName/";
private static final Map<String, SchemaTypeConfigProto> SCHEMA_MAP =
ImmutableMap.of(
PREFIX + SCHEMA_TYPE_1, SCHEMA_PROTO_1, PREFIX + SCHEMA_TYPE_2, SCHEMA_PROTO_2);
@Test @Test
public void testDocumentProtoConvert() { public void testDocumentProtoConvert() {
GenericDocument document = GenericDocument document =
new GenericDocument.Builder<GenericDocument.Builder<?>>( new GenericDocument.Builder<GenericDocument.Builder<?>>(
"namespace", "uri1", "schemaType1") "namespace", "uri1", SCHEMA_TYPE_1)
.setCreationTimestampMillis(5L) .setCreationTimestampMillis(5L)
.setScore(1) .setScore(1)
.setTtlMillis(1L) .setTtlMillis(1L)
@@ -66,7 +81,7 @@ public class GenericDocumentToProtoConverterTest {
DocumentProto.Builder documentProtoBuilder = DocumentProto.Builder documentProtoBuilder =
DocumentProto.newBuilder() DocumentProto.newBuilder()
.setUri("uri1") .setUri("uri1")
.setSchema("schemaType1") .setSchema(SCHEMA_TYPE_1)
.setCreationTimestampMs(5L) .setCreationTimestampMs(5L)
.setScore(1) .setScore(1)
.setTtlMs(1L) .setTtlMs(1L)
@@ -109,9 +124,133 @@ public class GenericDocumentToProtoConverterTest {
documentProtoBuilder.addProperties(propertyProtoMap.get(key)); documentProtoBuilder.addProperties(propertyProtoMap.get(key));
} }
DocumentProto documentProto = documentProtoBuilder.build(); DocumentProto documentProto = documentProtoBuilder.build();
assertThat(GenericDocumentToProtoConverter.toDocumentProto(document))
.isEqualTo(documentProto); GenericDocument convertedGenericDocument =
assertThat(document) GenericDocumentToProtoConverter.toGenericDocument(
.isEqualTo(GenericDocumentToProtoConverter.toGenericDocument(documentProto)); documentProto, PREFIX, SCHEMA_MAP);
DocumentProto convertedDocumentProto =
GenericDocumentToProtoConverter.toDocumentProto(document);
assertThat(convertedDocumentProto).isEqualTo(documentProto);
assertThat(convertedGenericDocument).isEqualTo(document);
}
@Test
public void testConvertDocument_whenPropertyHasEmptyList() {
String emptyStringPropertyName = "emptyStringProperty";
DocumentProto documentProto =
DocumentProto.newBuilder()
.setUri("uri1")
.setSchema(SCHEMA_TYPE_1)
.setCreationTimestampMs(5L)
.setNamespace("namespace")
.addProperties(
PropertyProto.newBuilder().setName(emptyStringPropertyName).build())
.build();
PropertyConfigProto emptyStringListProperty =
PropertyConfigProto.newBuilder()
.setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setPropertyName(emptyStringPropertyName)
.build();
SchemaTypeConfigProto schemaTypeConfigProto =
SchemaTypeConfigProto.newBuilder()
.addProperties(emptyStringListProperty)
.setSchemaType(SCHEMA_TYPE_1)
.build();
Map<String, SchemaTypeConfigProto> schemaMap =
ImmutableMap.of(PREFIX + SCHEMA_TYPE_1, schemaTypeConfigProto);
GenericDocument convertedDocument =
GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX, schemaMap);
GenericDocument expectedDocument =
new GenericDocument.Builder<GenericDocument.Builder<?>>(
"namespace", "uri1", SCHEMA_TYPE_1)
.setCreationTimestampMillis(5L)
.setPropertyString(emptyStringPropertyName)
.build();
assertThat(convertedDocument).isEqualTo(expectedDocument);
assertThat(expectedDocument.getPropertyStringArray(emptyStringPropertyName)).isEmpty();
}
@Test
public void testConvertDocument_whenNestedDocumentPropertyHasEmptyList() {
String emptyStringPropertyName = "emptyStringProperty";
String documentPropertyName = "documentProperty";
DocumentProto nestedDocumentProto =
DocumentProto.newBuilder()
.setUri("uri2")
.setSchema(SCHEMA_TYPE_2)
.setCreationTimestampMs(5L)
.setNamespace("namespace")
.addProperties(
PropertyProto.newBuilder().setName(emptyStringPropertyName).build())
.build();
DocumentProto documentProto =
DocumentProto.newBuilder()
.setUri("uri1")
.setSchema(SCHEMA_TYPE_1)
.setCreationTimestampMs(5L)
.setNamespace("namespace")
.addProperties(
PropertyProto.newBuilder()
.addDocumentValues(nestedDocumentProto)
.setName(documentPropertyName)
.build())
.build();
PropertyConfigProto documentProperty =
PropertyConfigProto.newBuilder()
.setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
.setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
.setPropertyName(documentPropertyName)
.setSchemaType(SCHEMA_TYPE_2)
.build();
SchemaTypeConfigProto schemaTypeConfigProto =
SchemaTypeConfigProto.newBuilder()
.addProperties(documentProperty)
.setSchemaType(SCHEMA_TYPE_1)
.build();
PropertyConfigProto emptyStringListProperty =
PropertyConfigProto.newBuilder()
.setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED)
.setDataType(PropertyConfigProto.DataType.Code.STRING)
.setPropertyName(emptyStringPropertyName)
.build();
SchemaTypeConfigProto nestedSchemaTypeConfigProto =
SchemaTypeConfigProto.newBuilder()
.addProperties(emptyStringListProperty)
.setSchemaType(SCHEMA_TYPE_2)
.build();
Map<String, SchemaTypeConfigProto> schemaMap =
ImmutableMap.of(
PREFIX + SCHEMA_TYPE_1,
schemaTypeConfigProto,
PREFIX + SCHEMA_TYPE_2,
nestedSchemaTypeConfigProto);
GenericDocument convertedDocument =
GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX, schemaMap);
GenericDocument expectedDocument =
new GenericDocument.Builder<GenericDocument.Builder<?>>(
"namespace", "uri1", SCHEMA_TYPE_1)
.setCreationTimestampMillis(5L)
.setPropertyDocument(
documentPropertyName,
new GenericDocument.Builder<GenericDocument.Builder<?>>(
"namespace", "uri2", SCHEMA_TYPE_2)
.setCreationTimestampMillis(5L)
.setPropertyString(emptyStringPropertyName)
.build())
.build();
assertThat(convertedDocument).isEqualTo(expectedDocument);
assertThat(
expectedDocument
.getPropertyDocument(documentPropertyName)
.getPropertyStringArray(emptyStringPropertyName))
.isEmpty();
} }
} }

View File

@@ -21,8 +21,10 @@ import static com.google.common.truth.Truth.assertThat;
import android.app.appsearch.SearchResult; import android.app.appsearch.SearchResult;
import android.app.appsearch.SearchResultPage; import android.app.appsearch.SearchResultPage;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
import com.android.server.appsearch.proto.DocumentProto; import com.android.server.appsearch.proto.DocumentProto;
import com.android.server.appsearch.proto.PropertyProto; import com.android.server.appsearch.proto.PropertyProto;
import com.android.server.appsearch.proto.SchemaTypeConfigProto;
import com.android.server.appsearch.proto.SearchResultProto; import com.android.server.appsearch.proto.SearchResultProto;
import com.android.server.appsearch.proto.SnippetMatchProto; import com.android.server.appsearch.proto.SnippetMatchProto;
import com.android.server.appsearch.proto.SnippetProto; import com.android.server.appsearch.proto.SnippetProto;
@@ -30,20 +32,29 @@ import com.android.server.appsearch.proto.SnippetProto;
import org.junit.Test; import org.junit.Test;
import java.util.Collections; import java.util.Collections;
import java.util.Map;
public class SnippetTest { public class SnippetTest {
private static final String SCHEMA_TYPE = "schema1";
private static final String PACKAGE_NAME = "packageName";
private static final String DATABASE_NAME = "databaseName";
private static final String PREFIX = PrefixUtil.createPrefix(PACKAGE_NAME, DATABASE_NAME);
private static final SchemaTypeConfigProto SCHEMA_TYPE_CONFIG_PROTO =
SchemaTypeConfigProto.newBuilder().setSchemaType(PREFIX + SCHEMA_TYPE).build();
private static final Map<String, Map<String, SchemaTypeConfigProto>> SCHEMA_MAP =
Collections.singletonMap(
PREFIX,
Collections.singletonMap(PREFIX + SCHEMA_TYPE, SCHEMA_TYPE_CONFIG_PROTO));
// TODO(tytytyww): Add tests for Double and Long Snippets. // TODO(tytytyww): Add tests for Double and Long Snippets.
@Test @Test
public void testSingleStringSnippet() { public void testSingleStringSnippet() {
final String propertyKeyString = "content"; final String propertyKeyString = "content";
final String propertyValueString = final String propertyValueString =
"A commonly used fake word is foo.\n" "A commonly used fake word is foo.\n"
+ " Another nonsense word that’s used a lot\n" + " Another nonsense word that’s used a lot\n"
+ " is bar.\n"; + " is bar.\n";
final String uri = "uri1"; final String uri = "uri1";
final String schemaType = "schema1";
final String searchWord = "foo"; final String searchWord = "foo";
final String exactMatch = "foo"; final String exactMatch = "foo";
final String window = "is foo"; final String window = "is foo";
@@ -57,7 +68,7 @@ public class SnippetTest {
DocumentProto documentProto = DocumentProto documentProto =
DocumentProto.newBuilder() DocumentProto.newBuilder()
.setUri(uri) .setUri(uri)
.setSchema(schemaType) .setSchema(SCHEMA_TYPE)
.addProperties(property) .addProperties(property)
.build(); .build();
SnippetProto snippetProto = SnippetProto snippetProto =
@@ -86,8 +97,9 @@ public class SnippetTest {
SearchResultPage searchResultPage = SearchResultPage searchResultPage =
SearchResultToProtoConverter.toSearchResultPage( SearchResultToProtoConverter.toSearchResultPage(
searchResultProto, searchResultProto,
Collections.singletonList("packageName"), Collections.singletonList(PACKAGE_NAME),
Collections.singletonList("databaseName")); Collections.singletonList(DATABASE_NAME),
SCHEMA_MAP);
for (SearchResult result : searchResultPage.getResults()) { for (SearchResult result : searchResultPage.getResults()) {
SearchResult.MatchInfo match = result.getMatches().get(0); SearchResult.MatchInfo match = result.getMatches().get(0);
assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString); assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString);
@@ -112,7 +124,6 @@ public class SnippetTest {
+ " Another nonsense word that’s used a lot\n" + " Another nonsense word that’s used a lot\n"
+ " is bar.\n"; + " is bar.\n";
final String uri = "uri1"; final String uri = "uri1";
final String schemaType = "schema1";
final String searchWord = "foo"; final String searchWord = "foo";
final String exactMatch = "foo"; final String exactMatch = "foo";
final String window = "is foo"; final String window = "is foo";
@@ -126,7 +137,7 @@ public class SnippetTest {
DocumentProto documentProto = DocumentProto documentProto =
DocumentProto.newBuilder() DocumentProto.newBuilder()
.setUri(uri) .setUri(uri)
.setSchema(schemaType) .setSchema(SCHEMA_TYPE)
.addProperties(property) .addProperties(property)
.build(); .build();
SearchResultProto.ResultProto resultProto = SearchResultProto.ResultProto resultProto =
@@ -137,8 +148,9 @@ public class SnippetTest {
SearchResultPage searchResultPage = SearchResultPage searchResultPage =
SearchResultToProtoConverter.toSearchResultPage( SearchResultToProtoConverter.toSearchResultPage(
searchResultProto, searchResultProto,
Collections.singletonList("packageName"), Collections.singletonList(PACKAGE_NAME),
Collections.singletonList("databaseName")); Collections.singletonList(DATABASE_NAME),
SCHEMA_MAP);
for (SearchResult result : searchResultPage.getResults()) { for (SearchResult result : searchResultPage.getResults()) {
assertThat(result.getMatches()).isEmpty(); assertThat(result.getMatches()).isEmpty();
} }
@@ -162,7 +174,7 @@ public class SnippetTest {
DocumentProto documentProto = DocumentProto documentProto =
DocumentProto.newBuilder() DocumentProto.newBuilder()
.setUri("uri1") .setUri("uri1")
.setSchema("schema1") .setSchema(SCHEMA_TYPE)
.addProperties(property1) .addProperties(property1)
.addProperties(property2) .addProperties(property2)
.build(); .build();
@@ -203,8 +215,9 @@ public class SnippetTest {
SearchResultPage searchResultPage = SearchResultPage searchResultPage =
SearchResultToProtoConverter.toSearchResultPage( SearchResultToProtoConverter.toSearchResultPage(
searchResultProto, searchResultProto,
Collections.singletonList("packageName"), Collections.singletonList(PACKAGE_NAME),
Collections.singletonList("databaseName")); Collections.singletonList(DATABASE_NAME),
SCHEMA_MAP);
for (SearchResult result : searchResultPage.getResults()) { for (SearchResult result : searchResultPage.getResults()) {
SearchResult.MatchInfo match1 = result.getMatches().get(0); SearchResult.MatchInfo match1 = result.getMatches().get(0);