Merge "Update framework from jetpack." into sc-dev am: 9f9cc640df

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14669187

Change-Id: Iee3c1624de33a451e503fc9f1074d372b5c826a0
This commit is contained in:
Alexander Dorokhine
2021-05-21 18:25:05 +00:00
committed by Automerger Merge Worker
14 changed files with 258 additions and 63 deletions

View File

@@ -206,7 +206,7 @@ package android.app.appsearch {
method @NonNull public android.app.appsearch.GetByDocumentIdRequest build();
}
public class GetSchemaResponse {
public final class GetSchemaResponse {
method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas();
method @IntRange(from=0) public int getVersion();
}

View File

@@ -389,12 +389,24 @@ public final class AppSearchSchema {
public @interface TokenizerType {}
/**
* It is only valid for tokenizer_type to be 'NONE' if {@link #getIndexingType} is {@link
* This value indicates that no tokens should be extracted from this property.
*
* <p>It is only valid for tokenizer_type to be 'NONE' if {@link #getIndexingType} is {@link
* #INDEXING_TYPE_NONE}.
*/
public static final int TOKENIZER_TYPE_NONE = 0;
/** Tokenization for plain text. */
/**
* Tokenization for plain text. This value indicates that tokens should be extracted from
* this property based on word breaks. Segments of whitespace and punctuation are not
* considered tokens.
*
* <p>Ex. A property with "foo bar. baz." will produce tokens for "foo", "bar" and "baz".
* The segments " " and "." will not be considered tokens.
*
* <p>It is only valid for tokenizer_type to be 'PLAIN' if {@link #getIndexingType} is
* {@link #INDEXING_TYPE_EXACT_TERMS} or {@link #INDEXING_TYPE_PREFIXES}.
*/
public static final int TOKENIZER_TYPE_PLAIN = 1;
StringPropertyConfig(@NonNull Bundle bundle) {
@@ -474,6 +486,17 @@ public final class AppSearchSchema {
/** Constructs a new {@link StringPropertyConfig} from the contents of this builder. */
@NonNull
public StringPropertyConfig build() {
if (mTokenizerType == TOKENIZER_TYPE_NONE) {
Preconditions.checkState(
mIndexingType == INDEXING_TYPE_NONE,
"Cannot set "
+ "TOKENIZER_TYPE_NONE with an indexing type other than "
+ "INDEXING_TYPE_NONE.");
} else {
Preconditions.checkState(
mIndexingType != INDEXING_TYPE_NONE,
"Cannot set " + "TOKENIZER_TYPE_PLAIN with INDEXING_TYPE_NONE.");
}
Bundle bundle = new Bundle();
bundle.putString(NAME_FIELD, mPropertyName);
bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_STRING);

View File

@@ -27,8 +27,6 @@ import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import com.android.internal.util.Preconditions;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
@@ -1018,17 +1016,14 @@ public class GenericDocument {
// GenericDocument.
@SuppressLint("StaticFinalBuilder")
public static class Builder<BuilderType extends Builder> {
private final Bundle mBundle;
private final Bundle mProperties;
private Bundle mBundle;
private Bundle mProperties;
private final BuilderType mBuilderTypeInstance;
private boolean mBuilt = false;
/**
* Creates a new {@link GenericDocument.Builder}.
*
* <p>Once {@link #build} is called, the instance can no longer be used.
*
* <p>Document IDs are unique within a namespace.
*
* <p>The number of namespaces per app should be kept small for efficiency reasons.
@@ -1053,9 +1048,6 @@ public class GenericDocument {
mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace);
mBundle.putString(GenericDocument.ID_FIELD, id);
mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType);
// Set current timestamp for creation timestamp by default.
mBundle.putLong(
GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis());
mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS);
mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE);
@@ -1063,7 +1055,11 @@ public class GenericDocument {
mBundle.putBundle(PROPERTIES_FIELD, mProperties);
}
/** Creates a new {@link GenericDocument.Builder} from the given Bundle. */
/**
* Creates a new {@link GenericDocument.Builder} from the given Bundle.
*
* <p>The bundle is NOT copied.
*/
@SuppressWarnings("unchecked")
Builder(@NonNull Bundle bundle) {
mBundle = Objects.requireNonNull(bundle);
@@ -1079,13 +1075,12 @@ public class GenericDocument {
*
* <p>The number of namespaces per app should be kept small for efficiency reasons.
*
* @throws IllegalStateException if the builder has already been used.
* @hide
*/
@NonNull
public BuilderType setNamespace(@NonNull String namespace) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(namespace);
resetIfBuilt();
mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace);
return mBuilderTypeInstance;
}
@@ -1096,13 +1091,12 @@ public class GenericDocument {
*
* <p>Document IDs are unique within a namespace.
*
* @throws IllegalStateException if the builder has already been used.
* @hide
*/
@NonNull
public BuilderType setId(@NonNull String id) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(id);
resetIfBuilt();
mBundle.putString(GenericDocument.ID_FIELD, id);
return mBuilderTypeInstance;
}
@@ -1113,13 +1107,12 @@ public class GenericDocument {
* <p>To successfully index a document, the schema type must match the name of an {@link
* AppSearchSchema} object previously provided to {@link AppSearchSession#setSchema}.
*
* @throws IllegalStateException if the builder has already been used.
* @hide
*/
@NonNull
public BuilderType setSchemaType(@NonNull String schemaType) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(schemaType);
resetIfBuilt();
mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType);
return mBuilderTypeInstance;
}
@@ -1136,14 +1129,13 @@ public class GenericDocument {
* <p>Any non-negative integer can be used a score. By default, scores are set to 0.
*
* @param score any non-negative {@code int} representing the document's score.
* @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setScore(@IntRange(from = 0, to = Integer.MAX_VALUE) int score) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
if (score < 0) {
throw new IllegalArgumentException("Document score cannot be negative.");
}
resetIfBuilt();
mBundle.putInt(GenericDocument.SCORE_FIELD, score);
return mBuilderTypeInstance;
}
@@ -1154,13 +1146,14 @@ public class GenericDocument {
* <p>This should be set using a value obtained from the {@link System#currentTimeMillis}
* time base.
*
* <p>If this method is not called, this will be set to the time the object is built.
*
* @param creationTimestampMillis a creation timestamp in milliseconds.
* @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setCreationTimestampMillis(
@CurrentTimeMillisLong long creationTimestampMillis) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
resetIfBuilt();
mBundle.putLong(
GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, creationTimestampMillis);
return mBuilderTypeInstance;
@@ -1177,14 +1170,13 @@ public class GenericDocument {
* auto-deleted until the app is uninstalled or {@link AppSearchSession#remove} is called.
*
* @param ttlMillis a non-negative duration in milliseconds.
* @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setTtlMillis(long ttlMillis) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
if (ttlMillis < 0) {
throw new IllegalArgumentException("Document ttlMillis cannot be negative.");
}
resetIfBuilt();
mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, ttlMillis);
return mBuilderTypeInstance;
}
@@ -1197,13 +1189,12 @@ public class GenericDocument {
* @param values the {@code String} values of the property.
* @throws IllegalArgumentException if no values are provided, if provided values exceed
* maximum repeated property length, or if a passed in {@code String} is {@code null}.
* @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyString(@NonNull String name, @NonNull String... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(name);
Objects.requireNonNull(values);
resetIfBuilt();
putInPropertyBundle(name, values);
return mBuilderTypeInstance;
}
@@ -1216,13 +1207,12 @@ public class GenericDocument {
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@code boolean} values of the property.
* @throws IllegalArgumentException if values exceed maximum repeated property length.
* @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyBoolean(@NonNull String name, @NonNull boolean... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(name);
Objects.requireNonNull(values);
resetIfBuilt();
putInPropertyBundle(name, values);
return mBuilderTypeInstance;
}
@@ -1234,13 +1224,12 @@ public class GenericDocument {
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@code long} values of the property.
* @throws IllegalArgumentException if values exceed maximum repeated property length.
* @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyLong(@NonNull String name, @NonNull long... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(name);
Objects.requireNonNull(values);
resetIfBuilt();
putInPropertyBundle(name, values);
return mBuilderTypeInstance;
}
@@ -1252,13 +1241,12 @@ public class GenericDocument {
* property as given in {@link AppSearchSchema.PropertyConfig#getName}.
* @param values the {@code double} values of the property.
* @throws IllegalArgumentException if values exceed maximum repeated property length.
* @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyDouble(@NonNull String name, @NonNull double... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(name);
Objects.requireNonNull(values);
resetIfBuilt();
putInPropertyBundle(name, values);
return mBuilderTypeInstance;
}
@@ -1271,13 +1259,12 @@ public class GenericDocument {
* @param values the {@code byte[]} of the property.
* @throws IllegalArgumentException if no values are provided, if provided values exceed
* maximum repeated property length, or if a passed in {@code byte[]} is {@code null}.
* @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyBytes(@NonNull String name, @NonNull byte[]... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(name);
Objects.requireNonNull(values);
resetIfBuilt();
putInPropertyBundle(name, values);
return mBuilderTypeInstance;
}
@@ -1292,14 +1279,13 @@ public class GenericDocument {
* @throws IllegalArgumentException if no values are provided, if provided values exceed if
* provided values exceed maximum repeated property length, or if a passed in {@link
* GenericDocument} is {@code null}.
* @throws IllegalStateException if the builder has already been used.
*/
@NonNull
public BuilderType setPropertyDocument(
@NonNull String name, @NonNull GenericDocument... values) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(name);
Objects.requireNonNull(values);
resetIfBuilt();
putInPropertyBundle(name, values);
return mBuilderTypeInstance;
}
@@ -1314,8 +1300,8 @@ public class GenericDocument {
*/
@NonNull
public BuilderType clearProperty(@NonNull String name) {
Preconditions.checkState(!mBuilt, "Builder has already been used");
Objects.requireNonNull(name);
resetIfBuilt();
mProperties.remove(name);
return mBuilderTypeInstance;
}
@@ -1399,16 +1385,25 @@ public class GenericDocument {
}
}
/**
* Builds the {@link GenericDocument} object.
*
* @throws IllegalStateException if the builder has already been used.
*/
/** Builds the {@link GenericDocument} object. */
@NonNull
public GenericDocument build() {
Preconditions.checkState(!mBuilt, "Builder has already been used");
mBuilt = true;
// Set current timestamp for creation timestamp by default.
if (mBundle.getLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, -1) == -1) {
mBundle.putLong(
GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD,
System.currentTimeMillis());
}
return new GenericDocument(mBundle);
}
private void resetIfBuilt() {
if (mBuilt) {
mBundle = BundleUtil.deepCopy(mBundle);
mProperties = mBundle.getBundle(PROPERTIES_FIELD);
mBuilt = false;
}
}
}
}

View File

@@ -26,7 +26,7 @@ import java.util.Objects;
import java.util.Set;
/** The response class of {@link AppSearchSession#getSchema} */
public class GetSchemaResponse {
public final class GetSchemaResponse {
private static final String VERSION_FIELD = "version";
private static final String SCHEMAS_FIELD = "schemas";

View File

@@ -322,6 +322,8 @@ public final class SetSchemaRequest {
/**
* Sets a Map of {@link Migrator}s.
*
* <p>The key of the map is the schema type that the {@link Migrator} value applies to.
*
* <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type
* from the current version number stored in AppSearch to the final version set via {@link
* #setVersion}.
@@ -335,7 +337,8 @@ public final class SetSchemaRequest {
* SetSchemaRequest}.
*
* @param migrators A {@link Map} of migrators that translate a document from it's current
* version to the final version set via {@link #setVersion}.
* version to the final version set via {@link #setVersion}. The key of the map is the
* schema type that the {@link Migrator} value applies to.
* @see SetSchemaRequest.Builder#setVersion
* @see SetSchemaRequest.Builder#addSchemas
* @see AppSearchSession#setSchema

View File

@@ -930,7 +930,12 @@ public class AppSearchManagerService extends SystemService {
for (int i = 0; i < ids.size(); i++) {
String id = ids.get(i);
try {
impl.remove(packageName, databaseName, namespace, id);
impl.remove(
packageName,
databaseName,
namespace,
id,
/*removeStatsBuilder=*/ null);
++operationSuccessCount;
resultBuilder.setSuccess(id, /*result= */ null);
} catch (Throwable t) {
@@ -1007,7 +1012,8 @@ public class AppSearchManagerService extends SystemService {
packageName,
databaseName,
queryExpression,
new SearchSpec(searchSpecBundle));
new SearchSpec(searchSpecBundle),
/*removeStatsBuilder=*/ null);
// Now that the batch has been written. Persist the newly written data.
impl.persistToDisk(PersistType.Code.LITE);
++operationSuccessCount;

View File

@@ -58,6 +58,7 @@ import com.android.server.appsearch.external.localstorage.converter.SetSchemaRes
import com.android.server.appsearch.external.localstorage.converter.TypePropertyPathToProtoConverter;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
import com.google.android.icing.IcingSearchEngine;
@@ -1118,14 +1119,17 @@ public final class AppSearchImpl implements Closeable {
* @param databaseName The databaseName the document is in.
* @param namespace Namespace of the document to remove.
* @param id ID of the document to remove.
* @param removeStatsBuilder builder for {@link RemoveStats} to hold stats for remove
* @throws AppSearchException on IcingSearchEngine error.
*/
public void remove(
@NonNull String packageName,
@NonNull String databaseName,
@NonNull String namespace,
@NonNull String id)
@NonNull String id,
@Nullable RemoveStats.Builder removeStatsBuilder)
throws AppSearchException {
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
mReadWriteLock.writeLock().lock();
try {
throwIfClosedLocked();
@@ -1138,9 +1142,20 @@ public final class AppSearchImpl implements Closeable {
mIcingSearchEngineLocked.delete(prefixedNamespace, id);
mLogUtil.piiTrace(
"removeById, response", deleteResultProto.getStatus(), deleteResultProto);
if (removeStatsBuilder != null) {
removeStatsBuilder.setStatusCode(
statusProtoToResultCode(deleteResultProto.getStatus()));
AppSearchLoggerHelper.copyNativeStats(
deleteResultProto.getDeleteStats(), removeStatsBuilder);
}
checkSuccess(deleteResultProto.getStatus());
} finally {
mReadWriteLock.writeLock().unlock();
if (removeStatsBuilder != null) {
removeStatsBuilder.setTotalLatencyMillis(
(int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis));
}
}
}
@@ -1153,14 +1168,17 @@ public final class AppSearchImpl implements Closeable {
* @param databaseName The databaseName the document is in.
* @param queryExpression Query String to search.
* @param searchSpec Defines what and how to remove
* @param removeStatsBuilder builder for {@link RemoveStats} to hold stats for remove
* @throws AppSearchException on IcingSearchEngine error.
*/
public void removeByQuery(
@NonNull String packageName,
@NonNull String databaseName,
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
@NonNull SearchSpec searchSpec,
@Nullable RemoveStats.Builder removeStatsBuilder)
throws AppSearchException {
long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime();
mReadWriteLock.writeLock().lock();
try {
throwIfClosedLocked();
@@ -1195,12 +1213,24 @@ public final class AppSearchImpl implements Closeable {
mLogUtil.piiTrace(
"removeByQuery, response", deleteResultProto.getStatus(), deleteResultProto);
if (removeStatsBuilder != null) {
removeStatsBuilder.setStatusCode(
statusProtoToResultCode(deleteResultProto.getStatus()));
// TODO(b/187206766) also log query stats here once IcingLib returns it
AppSearchLoggerHelper.copyNativeStats(
deleteResultProto.getDeleteStats(), removeStatsBuilder);
}
// It seems that the caller wants to get success if the data matching the query is
// not in the DB because it was not there or was successfully deleted.
checkCodeOneOf(
deleteResultProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
} finally {
mReadWriteLock.writeLock().unlock();
if (removeStatsBuilder != null) {
removeStatsBuilder.setTotalLatencyMillis(
(int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis));
}
}
}

View File

@@ -22,6 +22,7 @@ import android.app.appsearch.exceptions.AppSearchException;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
/**
@@ -47,5 +48,8 @@ public interface AppSearchLogger {
/** Logs {@link SearchStats} */
void logStats(@NonNull SearchStats stats) throws AppSearchException;
/** Logs {@link RemoveStats} */
void logStats(@NonNull RemoveStats stats) throws AppSearchException;
// TODO(b/173532925) Add remaining logStats once we add all the stats.
}

View File

@@ -20,8 +20,10 @@ import android.annotation.NonNull;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
import com.google.android.icing.proto.DeleteStatsProto;
import com.google.android.icing.proto.InitializeStatsProto;
import com.google.android.icing.proto.PutDocumentStatsProto;
import com.google.android.icing.proto.QueryStatsProto;
@@ -119,4 +121,21 @@ public final class AppSearchLoggerHelper {
.setDocumentRetrievingLatencyMillis(
fromNativeStats.getDocumentRetrievalLatencyMs());
}
/*
* Copy native Query stats to buiilder.
*
* @param fromNativeStats Stats copied from.
* @param toStatsBuilder Stats copied to.
*/
static void copyNativeStats(
@NonNull DeleteStatsProto fromNativeStats,
@NonNull RemoveStats.Builder toStatsBuilder) {
Objects.requireNonNull(fromNativeStats);
Objects.requireNonNull(toStatsBuilder);
toStatsBuilder
.setNativeLatencyMillis(fromNativeStats.getLatencyMs())
.setDeleteType(fromNativeStats.getDeleteType().getNumber())
.setDeletedDocumentCount(fromNativeStats.getNumDocumentsDeleted());
}
}

View File

@@ -52,6 +52,9 @@ public final class SearchStats {
// Searches the global documents. Including platform surfaceable and 3p-access.
public static final int VISIBILITY_SCOPE_GLOBAL = 2;
// TODO(b/173532925): Add a field searchType to indicate where the search is used(normal
// query vs in removeByQuery vs during migration)
@NonNull private final String mPackageName;
@Nullable private final String mDatabase;
/**

View File

@@ -34,6 +34,7 @@ import com.android.server.appsearch.external.localstorage.AppSearchLogger;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
import java.io.UnsupportedEncodingException;
@@ -213,6 +214,11 @@ public final class PlatformLogger implements AppSearchLogger {
}
}
@Override
public void logStats(@androidx.annotation.NonNull RemoveStats stats) throws AppSearchException {
// TODO(b/173532925): Log stats
}
/**
* Removes cached UID for package.
*

View File

@@ -1 +1 @@
c6630eba424d98dd54ece674e769d9b0b883e410
be6d5138cbd64d3fd401a83d30bf9ad22a6c2d17

View File

@@ -455,7 +455,8 @@ public class AppSearchImplTest {
// delete 999 documents, we will reach the threshold to trigger optimize() in next
// deletion.
for (int i = 0; i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1; i++) {
mAppSearchImpl.remove("package", "database", "namespace", "id" + i);
mAppSearchImpl.remove(
"package", "database", "namespace", "id" + i, /*removeStatsBuilder=*/ null);
}
// Updates the check for optimize counter, checkForOptimize() will be triggered since
@@ -475,7 +476,8 @@ public class AppSearchImplTest {
< AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
+ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL;
i++) {
mAppSearchImpl.remove("package", "database", "namespace", "id" + i);
mAppSearchImpl.remove(
"package", "database", "namespace", "id" + i, /*removeStatsBuilder=*/ null);
}
// updates the check for optimize counter, will reach both CHECK_OPTIMIZE_INTERVAL and
// OPTIMIZE_THRESHOLD_DOC_COUNT this time and trigger a optimize().
@@ -885,17 +887,20 @@ public class AppSearchImplTest {
.addFilterSchemas("FakeType")
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.build();
mAppSearchImpl.removeByQuery("package", "EmptyDatabase", "", searchSpec);
mAppSearchImpl.removeByQuery(
"package", "EmptyDatabase", "", searchSpec, /*statsBuilder=*/ null);
searchSpec =
new SearchSpec.Builder()
.addFilterNamespaces("FakeNamespace")
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.build();
mAppSearchImpl.removeByQuery("package", "EmptyDatabase", "", searchSpec);
mAppSearchImpl.removeByQuery(
"package", "EmptyDatabase", "", searchSpec, /*statsBuilder=*/ null);
searchSpec = new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
mAppSearchImpl.removeByQuery("package", "EmptyDatabase", "", searchSpec);
mAppSearchImpl.removeByQuery(
"package", "EmptyDatabase", "", searchSpec, /*statsBuilder=*/ null);
}
@Test
@@ -1661,8 +1666,7 @@ public class AppSearchImplTest {
context,
VisibilityStore.NO_OP_USER_ID,
/*globalQuerierPackage=*/ "",
/*logger
=*/ null);
/*logger=*/ null);
// Initial check that we could do something at first.
List<AppSearchSchema> schemas =
@@ -1768,7 +1772,8 @@ public class AppSearchImplTest {
expectThrows(
IllegalStateException.class,
() -> {
appSearchImpl.remove("package", "database", "namespace", "id");
appSearchImpl.remove(
"package", "database", "namespace", "id", /*statsBuilder=*/ null);
});
expectThrows(
@@ -1780,7 +1785,8 @@ public class AppSearchImplTest {
"query",
new SearchSpec.Builder()
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
.build());
.build(),
/*statsBuilder=*/ null);
});
expectThrows(
@@ -1894,7 +1900,7 @@ public class AppSearchImplTest {
assertThat(getResult).isEqualTo(document2);
// Delete the first document
appSearchImpl.remove("package", "database", "namespace1", "id1");
appSearchImpl.remove("package", "database", "namespace1", "id1", /*statsBuilder=*/ null);
appSearchImpl.persistToDisk(PersistType.Code.LITE);
expectThrows(
AppSearchException.class,
@@ -1983,7 +1989,8 @@ public class AppSearchImplTest {
new SearchSpec.Builder()
.addFilterNamespaces("namespace1")
.setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY)
.build());
.build(),
/*statsBuilder=*/ null);
appSearchImpl.persistToDisk(PersistType.Code.LITE);
expectThrows(
AppSearchException.class,

View File

@@ -32,7 +32,9 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.server.appsearch.external.localstorage.stats.CallStats;
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.android.server.appsearch.external.localstorage.stats.RemoveStats;
import com.android.server.appsearch.external.localstorage.stats.SearchStats;
import com.android.server.appsearch.proto.DeleteStatsProto;
import com.android.server.appsearch.proto.InitializeStatsProto;
import com.android.server.appsearch.proto.PutDocumentStatsProto;
import com.android.server.appsearch.proto.QueryStatsProto;
@@ -73,6 +75,7 @@ public class AppSearchLoggerTest {
@Nullable PutDocumentStats mPutDocumentStats;
@Nullable InitializeStats mInitializeStats;
@Nullable SearchStats mSearchStats;
@Nullable RemoveStats mRemoveStats;
@Override
public void logStats(@NonNull CallStats stats) {
@@ -93,6 +96,11 @@ public class AppSearchLoggerTest {
public void logStats(@NonNull SearchStats stats) {
mSearchStats = stats;
}
@Override
public void logStats(@NonNull RemoveStats stats) {
mRemoveStats = stats;
}
}
@Test
@@ -252,6 +260,27 @@ public class AppSearchLoggerTest {
.isEqualTo(nativeDocumentRetrievingLatencyMillis);
}
@Test
public void testAppSearchLoggerHelper_testCopyNativeStats_remove() {
final int nativeLatencyMillis = 1;
final int nativeDeleteType = 2;
final int nativeNumDocumentDeleted = 3;
DeleteStatsProto nativeDeleteStatsProto =
DeleteStatsProto.newBuilder()
.setLatencyMs(nativeLatencyMillis)
.setDeleteType(DeleteStatsProto.DeleteType.Code.forNumber(nativeDeleteType))
.setNumDocumentsDeleted(nativeNumDocumentDeleted)
.build();
RemoveStats.Builder rBuilder = new RemoveStats.Builder("packageName", "database");
AppSearchLoggerHelper.copyNativeStats(nativeDeleteStatsProto, rBuilder);
RemoveStats rStats = rBuilder.build();
assertThat(rStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
assertThat(rStats.getDeleteType()).isEqualTo(nativeDeleteType);
assertThat(rStats.getDeletedDocumentCount()).isEqualTo(nativeNumDocumentDeleted);
}
//
// Testing actual logging
//
@@ -355,4 +384,74 @@ public class AppSearchLoggerTest {
assertThat(sStats.isFirstPage()).isTrue();
assertThat(sStats.getScoredDocumentCount()).isEqualTo(1);
}
@Test
public void testLoggingStats_remove() throws Exception {
// Insert schema
final String testPackageName = "testPackage";
final String testDatabase = "testDatabase";
final String testNamespace = "testNameSpace";
final String testId = "id";
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
mAppSearchImpl.setSchema(
testPackageName,
testDatabase,
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
GenericDocument document =
new GenericDocument.Builder<>(testNamespace, testId, "type").build();
mAppSearchImpl.putDocument(testPackageName, testDatabase, document, /*logger=*/ null);
RemoveStats.Builder rStatsBuilder = new RemoveStats.Builder(testPackageName, testDatabase);
mAppSearchImpl.remove(testPackageName, testDatabase, testNamespace, testId, rStatsBuilder);
RemoveStats rStats = rStatsBuilder.build();
assertThat(rStats.getPackageName()).isEqualTo(testPackageName);
assertThat(rStats.getDatabase()).isEqualTo(testDatabase);
// delete by namespace + id
assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.SINGLE_VALUE);
assertThat(rStats.getDeletedDocumentCount()).isEqualTo(1);
}
@Test
public void testLoggingStats_removeByQuery() throws Exception {
// Insert schema
final String testPackageName = "testPackage";
final String testDatabase = "testDatabase";
final String testNamespace = "testNameSpace";
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
mAppSearchImpl.setSchema(
testPackageName,
testDatabase,
schemas,
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*schemasPackageAccessible=*/ Collections.emptyMap(),
/*forceOverride=*/ false,
/*version=*/ 0);
GenericDocument document1 =
new GenericDocument.Builder<>(testNamespace, "id1", "type").build();
GenericDocument document2 =
new GenericDocument.Builder<>(testNamespace, "id2", "type").build();
mAppSearchImpl.putDocument(testPackageName, testDatabase, document1, mLogger);
mAppSearchImpl.putDocument(testPackageName, testDatabase, document2, mLogger);
// No query filters specified. package2 should only get its own documents back.
SearchSpec searchSpec =
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
RemoveStats.Builder rStatsBuilder = new RemoveStats.Builder(testPackageName, testDatabase);
mAppSearchImpl.removeByQuery(
testPackageName, testDatabase, /*queryExpression=*/ "", searchSpec, rStatsBuilder);
RemoveStats rStats = rStatsBuilder.build();
assertThat(rStats.getPackageName()).isEqualTo(testPackageName);
assertThat(rStats.getDatabase()).isEqualTo(testDatabase);
// delete by query
assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.QUERY_VALUE);
assertThat(rStats.getDeletedDocumentCount()).isEqualTo(2);
}
}