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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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}
* calls.
*
* <p><b>NOTE:</b>By default, documents are removed via a soft delete operation. Once the
* document crosses the count threshold or byte usage threshold, the documents will be removed
* from disk.
* <p>Once the database crosses the document count or byte usage threshold, removed documents
* will be deleted from disk.
*
* @param request {@link RemoveByUriRequest} with URIs and namespace to remove from the index.
* @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The

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 com.android.server.appsearch.external.localstorage.util.PrefixUtil;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -203,7 +205,7 @@ public class AppSearchImplPlatformTest {
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
// Set schema1
String prefix = AppSearchImpl.createPrefix("package", "database");
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
@@ -280,7 +282,7 @@ public class AppSearchImplPlatformTest {
mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
String prefix = AppSearchImpl.createPrefix("package", "database");
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
@@ -353,7 +355,7 @@ public class AppSearchImplPlatformTest {
@Test
public void testSetSchema_defaultPlatformVisible() throws Exception {
String prefix = AppSearchImpl.createPrefix("package", "database");
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
@@ -372,7 +374,7 @@ public class AppSearchImplPlatformTest {
@Test
public void testSetSchema_platformHidden() throws Exception {
String prefix = AppSearchImpl.createPrefix("package", "database");
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
@@ -391,7 +393,7 @@ public class AppSearchImplPlatformTest {
@Test
public void testSetSchema_defaultNotPackageAccessible() throws Exception {
String prefix = AppSearchImpl.createPrefix("package", "database");
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",
@@ -419,7 +421,7 @@ public class AppSearchImplPlatformTest {
mMockPackageManager.mockGetPackageUidAsUser(packageNameFoo, mContext.getUserId(), uidFoo);
mMockPackageManager.mockAddSigningCertificate(packageNameFoo, sha256CertFoo);
String prefix = AppSearchImpl.createPrefix("package", "database");
String prefix = PrefixUtil.createPrefix("package", "database");
mAppSearchImpl.setSchema(
"package",
"database",

View File

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

View File

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

View File

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

View File

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