Update Framework from Jetpack.
Changes included:
* 517c1ee: Schema Migration API updates to match API council recommendations.
* 407144d: Refactor some migration helper methods to Utils class.
* 2297b3a: Add a test for migrate to a nonexistent schema.
* 16a38d0: Log the PutDocumentStats in AppSearchImpl
Bug: 178060626
Bug: 179804862
Bug: 173532925
Test: Presubmit
Change-Id: I8b879bc9a5f30f9be3ce08d07dc60f4b177fb11b
diff --git a/framework/api/current.txt b/framework/api/current.txt
index f6bfaa3..e08d22c 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -24,14 +24,6 @@
method @Deprecated @NonNull public android.app.appsearch.AppSearchManager.SearchContext.Builder setDatabaseName(@NonNull String);
}
- public interface AppSearchMigrationHelper {
- method public void queryAndTransform(@NonNull String, @NonNull android.app.appsearch.AppSearchMigrationHelper.Transformer) throws java.lang.Exception;
- }
-
- public static interface AppSearchMigrationHelper.Transformer {
- method @NonNull public android.app.appsearch.GenericDocument transform(int, int, @NonNull android.app.appsearch.GenericDocument) throws java.lang.Exception;
- }
-
public final class AppSearchResult<ValueType> {
method @Nullable public String getErrorMessage();
method public int getResultCode();
@@ -109,11 +101,6 @@
method @NonNull public android.app.appsearch.AppSearchSchema.Int64PropertyConfig.Builder setCardinality(int);
}
- public static interface AppSearchSchema.Migrator {
- method public default void onDowngrade(int, int, @NonNull android.app.appsearch.AppSearchMigrationHelper) throws java.lang.Exception;
- method public default void onUpgrade(int, int, @NonNull android.app.appsearch.AppSearchMigrationHelper) throws java.lang.Exception;
- }
-
public abstract static class AppSearchSchema.PropertyConfig {
method public int getCardinality();
method @NonNull public String getName();
@@ -221,6 +208,13 @@
method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec);
}
+ public abstract class Migrator {
+ ctor public Migrator();
+ ctor public Migrator(int);
+ method @NonNull @WorkerThread public abstract android.app.appsearch.GenericDocument onDowngrade(int, int, @NonNull android.app.appsearch.GenericDocument);
+ method @NonNull @WorkerThread public abstract android.app.appsearch.GenericDocument onUpgrade(int, int, @NonNull android.app.appsearch.GenericDocument);
+ }
+
public class PackageIdentifier {
ctor public PackageIdentifier(@NonNull String, @NonNull byte[]);
method @NonNull public String getPackageName();
@@ -357,7 +351,7 @@
}
public final class SetSchemaRequest {
- method @NonNull public java.util.Map<java.lang.String,android.app.appsearch.AppSearchSchema.Migrator> getMigrators();
+ method @NonNull public java.util.Map<java.lang.String,android.app.appsearch.Migrator> getMigrators();
method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas();
method @NonNull public java.util.Set<java.lang.String> getSchemasNotDisplayedBySystem();
method @Deprecated @NonNull public java.util.Set<java.lang.String> getSchemasNotVisibleToSystemUi();
@@ -371,7 +365,7 @@
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchemas(@NonNull java.util.Collection<android.app.appsearch.AppSearchSchema>);
method @NonNull public android.app.appsearch.SetSchemaRequest build();
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean);
- method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrator(@NonNull String, @NonNull android.app.appsearch.AppSearchSchema.Migrator);
+ method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrator(@NonNull String, @NonNull android.app.appsearch.Migrator);
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeDisplayedBySystem(@NonNull String, boolean);
method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(@NonNull String, boolean, @NonNull android.app.appsearch.PackageIdentifier);
method @Deprecated @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForSystemUi(@NonNull String, boolean);
diff --git a/framework/java/android/app/appsearch/AppSearchSession.java b/framework/java/android/app/appsearch/AppSearchSession.java
index 486acb4..9ea73a9 100644
--- a/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/framework/java/android/app/appsearch/AppSearchSession.java
@@ -114,8 +114,6 @@
* @param executor Executor on which to invoke the callback.
* @param callback Callback to receive errors resulting from setting the schema. If the
* operation succeeds, the callback will be invoked with {@code null}.
- * @see android.app.appsearch.AppSearchSchema.Migrator
- * @see android.app.appsearch.AppSearchMigrationHelper.Transformer
*/
// TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
// exposed.
diff --git a/framework/java/external/android/app/appsearch/AppSearchMigrationHelper.java b/framework/java/external/android/app/appsearch/AppSearchMigrationHelper.java
deleted file mode 100644
index 37943fc..0000000
--- a/framework/java/external/android/app/appsearch/AppSearchMigrationHelper.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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 android.app.appsearch;
-
-import android.annotation.NonNull;
-import android.annotation.SuppressLint;
-
-/**
- * The helper class for {@link AppSearchSchema} migration.
- *
- * <p>It will query and migrate {@link GenericDocument} in given type to a new version.
- */
-public interface AppSearchMigrationHelper {
-
- /**
- * Queries all documents that need to be migrated to the different version, and transform
- * documents to that version by passing them to the provided {@link Transformer}.
- *
- * @param schemaType The schema that need be updated and migrated {@link GenericDocument} under
- * this type.
- * @param transformer The {@link Transformer} that will upgrade or downgrade a {@link
- * GenericDocument} to new version.
- * @see Transformer#transform
- */
- // Rethrow the Generic Exception thrown from the Transformer.
- @SuppressLint("GenericException")
- void queryAndTransform(@NonNull String schemaType, @NonNull Transformer transformer)
- throws Exception;
-
- /** The class to migrate {@link GenericDocument} between different version. */
- interface Transformer {
-
- /**
- * Translates a {@link GenericDocument} from a version to a different version.
- *
- * <p>If the uri, schema type or namespace is changed via the transform, it will apply to
- * the new {@link GenericDocument}.
- *
- * @param currentVersion The current version of the document's schema.
- * @param finalVersion The final version that documents need to be migrated to.
- * @param document The {@link GenericDocument} need to be translated to new version.
- * @return A {@link GenericDocument} in new version.
- */
- @NonNull
- // This method will be overridden by users, allow them to throw any customer Exceptions.
- @SuppressLint("GenericException")
- GenericDocument transform(
- int currentVersion, int finalVersion, @NonNull GenericDocument document)
- throws Exception;
- }
-}
diff --git a/framework/java/external/android/app/appsearch/AppSearchSchema.java b/framework/java/external/android/app/appsearch/AppSearchSchema.java
index 2cf5271..55f0c80 100644
--- a/framework/java/external/android/app/appsearch/AppSearchSchema.java
+++ b/framework/java/external/android/app/appsearch/AppSearchSchema.java
@@ -20,7 +20,6 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.app.appsearch.exceptions.IllegalSchemaException;
import android.app.appsearch.util.BundleUtil;
import android.os.Bundle;
@@ -179,7 +178,7 @@
* @throws IllegalStateException if the version is negative or the builder has already been
* used.
* @see AppSearchSession#setSchema
- * @see AppSearchSchema.Migrator
+ * @see Migrator
* @see SetSchemaRequest.Builder#setMigrator
*/
@NonNull
@@ -861,43 +860,4 @@
}
}
}
-
- /**
- * A migrator class to translate {@link GenericDocument} from different version of {@link
- * AppSearchSchema}
- */
- public interface Migrator {
-
- /**
- * Migrates {@link GenericDocument} to a newer version of {@link AppSearchSchema}.
- *
- * <p>This methods will be invoked only if the {@link SetSchemaRequest} is setting a higher
- * version number than the current {@link AppSearchSchema} saved in AppSearch.
- *
- * @param currentVersion The current version of the document's schema.
- * @param targetVersion The final version that documents need to be migrated to.
- * @param helper The helper class could help to query all documents need to be migrated.
- */
- // This method will be overridden by users, allow them to throw any customer Exceptions.
- @SuppressLint("GenericException")
- default void onUpgrade(
- int currentVersion, int targetVersion, @NonNull AppSearchMigrationHelper helper)
- throws Exception {}
-
- /**
- * Migrates {@link GenericDocument} to an older version of {@link AppSearchSchema}.
- *
- * <p>The methods will be invoked only if the {@link SetSchemaRequest} is setting a higher
- * version number than the current {@link AppSearchSchema} saved in AppSearch.
- *
- * @param currentVersion The current version of the document's schema.
- * @param targetVersion The final version that documents need to be migrated to.
- * @param helper The helper class could help to query all documents need to be migrated.
- */
- // This method will be overridden by users, allow them to throw any customer Exceptions.
- @SuppressLint("GenericException")
- default void onDowngrade(
- int currentVersion, int targetVersion, @NonNull AppSearchMigrationHelper helper)
- throws Exception {}
- }
}
diff --git a/framework/java/external/android/app/appsearch/Migrator.java b/framework/java/external/android/app/appsearch/Migrator.java
new file mode 100644
index 0000000..5ae9a41
--- /dev/null
+++ b/framework/java/external/android/app/appsearch/Migrator.java
@@ -0,0 +1,107 @@
+/*
+ * 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 android.app.appsearch;
+
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A migrator class to translate {@link GenericDocument} from different version of {@link
+ * AppSearchSchema}
+ *
+ * <p>Make non-backwards-compatible changes will delete all stored documents in old schema. You can
+ * save your documents by setting {@link Migrator} via the {@link
+ * SetSchemaRequest.Builder#setMigrator} for each type and target version you want to save.
+ *
+ * <p>{@link #onDowngrade} or {@link #onUpgrade} will be triggered if the version number of the
+ * schema stored in AppSearch is different with the version in the request.
+ *
+ * <p>If any error or Exception occurred in the {@link #onDowngrade} or {@link #onUpgrade}, all the
+ * setSchema request will be rejected unless the schema changes are backwards-compatible, and stored
+ * documents won't have any observable changes.
+ */
+public abstract class Migrator {
+ private final int mStartVersion;
+
+ /**
+ * Creates a {@link Migrator} will trigger migration for any version less than the final version
+ * in the new schema.
+ */
+ public Migrator() {
+ this(/*startVersion=*/ 0);
+ }
+
+ /**
+ * Creates a {@link Migrator} with a non-negative start version.
+ *
+ * <p>Providing 0 will trigger migration for any version less than the final version in the new
+ * schema.
+ *
+ * @param startVersion The migration will be only triggered for those versions greater or equal
+ * to the given startVersion.
+ */
+ public Migrator(int startVersion) {
+ Preconditions.checkArgumentNonnegative(startVersion);
+ mStartVersion = startVersion;
+ }
+
+ /**
+ * @return {@code True} if the current version need to be migrated.
+ * @hide
+ */
+ public boolean shouldMigrateToFinalVersion(int currentVersion, int finalVersion) {
+ return currentVersion >= mStartVersion && currentVersion != finalVersion;
+ }
+
+ /**
+ * Migrates {@link GenericDocument} to a newer version of {@link AppSearchSchema}.
+ *
+ * <p>This method will be invoked only if the {@link SetSchemaRequest} is setting a higher
+ * version number than the current {@link AppSearchSchema} saved in AppSearch.
+ *
+ * <p>This method will be invoked on the background worker thread.
+ *
+ * @param currentVersion The current version of the document's schema.
+ * @param targetVersion The final version that documents need to be migrated to.
+ * @param document The {@link GenericDocument} need to be translated to new version.
+ * @return A {@link GenericDocument} in new version.
+ */
+ @WorkerThread
+ @NonNull
+ public abstract GenericDocument onUpgrade(
+ int currentVersion, int targetVersion, @NonNull GenericDocument document);
+
+ /**
+ * Migrates {@link GenericDocument} to an older version of {@link AppSearchSchema}.
+ *
+ * <p>This method will be invoked only if the {@link SetSchemaRequest} is setting a lower
+ * version number than the current {@link AppSearchSchema} saved in AppSearch.
+ *
+ * <p>This method will be invoked on the background worker thread.
+ *
+ * @param currentVersion The current version of the document's schema.
+ * @param targetVersion The final version that documents need to be migrated to.
+ * @param document The {@link GenericDocument} need to be translated to new version.
+ * @return A {@link GenericDocument} in new version.
+ */
+ @WorkerThread
+ @NonNull
+ public abstract GenericDocument onDowngrade(
+ int currentVersion, int targetVersion, @NonNull GenericDocument document);
+}
diff --git a/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/framework/java/external/android/app/appsearch/SetSchemaRequest.java
index c054063..c1eedcd 100644
--- a/framework/java/external/android/app/appsearch/SetSchemaRequest.java
+++ b/framework/java/external/android/app/appsearch/SetSchemaRequest.java
@@ -63,24 +63,33 @@
* android.app.appsearch.exceptions.AppSearchException}, with a message describing the
* incompatibility. As a result, the previously set schema will remain unchanged.
*
- * <p>Backward incompatible changes can be made by setting {@link
- * SetSchemaRequest.Builder#setForceOverride} method to {@code true}. This deletes all documents
- * that are incompatible with the new schema. The new schema is then saved and persisted to disk.
+ * <p>Backward incompatible changes can be made by :
+ *
+ * <ul>
+ * <li>setting {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. This
+ * deletes all documents that are incompatible with the new schema. The new schema is then
+ * saved and persisted to disk.
+ * <li>Add a {@link Migrator} for each incompatible type and make no deletion. The migrator will
+ * migrate documents from it's old schema version to the new version. Migrated types will be
+ * set into both {@link SetSchemaResponse#getIncompatibleTypes()} and {@link
+ * SetSchemaResponse#getMigratedTypes()}. See the migration section below.
+ * </ul>
*
* @see AppSearchSession#setSchema
+ * @see Migrator
*/
public final class SetSchemaRequest {
private final Set<AppSearchSchema> mSchemas;
private final Set<String> mSchemasNotDisplayedBySystem;
private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages;
- private final Map<String, AppSearchSchema.Migrator> mMigrators;
+ private final Map<String, Migrator> mMigrators;
private final boolean mForceOverride;
SetSchemaRequest(
@NonNull Set<AppSearchSchema> schemas,
@NonNull Set<String> schemasNotDisplayedBySystem,
@NonNull Map<String, Set<PackageIdentifier>> schemasVisibleToPackages,
- @NonNull Map<String, AppSearchSchema.Migrator> migrators,
+ @NonNull Map<String, Migrator> migrators,
boolean forceOverride) {
mSchemas = Preconditions.checkNotNull(schemas);
mSchemasNotDisplayedBySystem = Preconditions.checkNotNull(schemasNotDisplayedBySystem);
@@ -129,9 +138,12 @@
return copy;
}
- /** Returns the map of {@link android.app.appsearch.AppSearchSchema.Migrator}. */
+ /**
+ * Returns the map of {@link Migrator}, the key will be the schema type of the {@link Migrator}
+ * associated with.
+ */
@NonNull
- public Map<String, AppSearchSchema.Migrator> getMigrators() {
+ public Map<String, Migrator> getMigrators() {
return Collections.unmodifiableMap(mMigrators);
}
@@ -164,7 +176,7 @@
private final Set<String> mSchemasNotDisplayedBySystem = new ArraySet<>();
private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages =
new ArrayMap<>();
- private final Map<String, AppSearchSchema.Migrator> mMigrators = new ArrayMap<>();
+ private final Map<String, Migrator> mMigrators = new ArrayMap<>();
private boolean mForceOverride = false;
private boolean mBuilt = false;
@@ -295,7 +307,7 @@
}
/**
- * Sets the {@link android.app.appsearch.AppSearchSchema.Migrator}.
+ * Sets the {@link Migrator}.
*
* @param schemaType The schema type to set migrator on.
* @param migrator The migrator translate a document from it's old version to a new
@@ -303,8 +315,7 @@
*/
@NonNull
@SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects.
- public Builder setMigrator(
- @NonNull String schemaType, @NonNull AppSearchSchema.Migrator migrator) {
+ public Builder setMigrator(@NonNull String schemaType, @NonNull Migrator migrator) {
Preconditions.checkNotNull(schemaType);
Preconditions.checkNotNull(migrator);
mMigrators.put(schemaType, migrator);
diff --git a/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/framework/java/external/android/app/appsearch/SetSchemaResponse.java
index 98cd49b..d63e437 100644
--- a/framework/java/external/android/app/appsearch/SetSchemaResponse.java
+++ b/framework/java/external/android/app/appsearch/SetSchemaResponse.java
@@ -128,8 +128,8 @@
* Returns a {@link Set} of schema type whose new definitions set in the {@link
* AppSearchSession#setSchema} call were incompatible with the pre-existing schema.
*
- * <p>If a {@link android.app.appsearch.AppSearchSchema.Migrator} is provided for this type and
- * the migration is success triggered. The type will also appear in {@link #getMigratedTypes()}.
+ * <p>If a {@link Migrator} is provided for this type and the migration is success triggered.
+ * The type will also appear in {@link #getMigratedTypes()}.
*
* @see AppSearchSession#setSchema
* @see SetSchemaRequest.Builder#setForceOverride
diff --git a/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java b/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
new file mode 100644
index 0000000..fae8ad4
--- /dev/null
+++ b/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java
@@ -0,0 +1,118 @@
+/*
+ * 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 android.app.appsearch.util;
+
+import android.annotation.NonNull;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.Migrator;
+import android.app.appsearch.exceptions.AppSearchException;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utilities for schema migration.
+ *
+ * @hide
+ */
+public final class SchemaMigrationUtil {
+ private static final String TAG = "AppSearchMigrateUtil";
+
+ private SchemaMigrationUtil() {}
+
+ /**
+ * Finds out which incompatible schema type won't be migrated by comparing its current and final
+ * version number.
+ */
+ @NonNull
+ public static Set<String> getUnmigratedIncompatibleTypes(
+ @NonNull Set<String> incompatibleSchemaTypes,
+ @NonNull Map<String, Migrator> migrators,
+ @NonNull Map<String, Integer> currentVersionMap,
+ @NonNull Map<String, Integer> finalVersionMap)
+ throws AppSearchException {
+ Set<String> unmigratedSchemaTypes = new ArraySet<>();
+ for (String unmigratedSchemaType : incompatibleSchemaTypes) {
+ Integer currentVersion = currentVersionMap.get(unmigratedSchemaType);
+ Integer finalVersion = finalVersionMap.get(unmigratedSchemaType);
+ if (currentVersion == null) {
+ // impossible, we have done something wrong.
+ throw new AppSearchException(
+ AppSearchResult.RESULT_UNKNOWN_ERROR,
+ "Cannot find the current version number for schema type: "
+ + unmigratedSchemaType);
+ }
+ if (finalVersion == null) {
+ // The schema doesn't exist in the SetSchemaRequest.
+ unmigratedSchemaTypes.add(unmigratedSchemaType);
+ continue;
+ }
+ // we don't have migrator or won't trigger migration for this schema type.
+ Migrator migrator = migrators.get(unmigratedSchemaType);
+ if (migrator == null
+ || !migrator.shouldMigrateToFinalVersion(currentVersion, finalVersion)) {
+ unmigratedSchemaTypes.add(unmigratedSchemaType);
+ }
+ }
+ return Collections.unmodifiableSet(unmigratedSchemaTypes);
+ }
+
+ /**
+ * Triggers upgrade or downgrade migration for the given schema type if its version stored in
+ * AppSearch is different with the version in the request.
+ *
+ * @return {@code True} if we trigger the migration for the given type.
+ */
+ public static boolean shouldTriggerMigration(
+ @NonNull String schemaType,
+ @NonNull Migrator migrator,
+ @NonNull Map<String, Integer> currentVersionMap,
+ @NonNull Map<String, Integer> finalVersionMap)
+ throws AppSearchException {
+ Integer currentVersion = currentVersionMap.get(schemaType);
+ Integer finalVersion = finalVersionMap.get(schemaType);
+ if (currentVersion == null) {
+ Log.d(TAG, "The SchemaType: " + schemaType + " not present in AppSearch.");
+ return false;
+ }
+ if (finalVersion == null) {
+ throw new AppSearchException(
+ AppSearchResult.RESULT_INVALID_ARGUMENT,
+ "Receive a migrator for schema type : "
+ + schemaType
+ + ", but the schema doesn't exist in the request.");
+ }
+ return migrator.shouldMigrateToFinalVersion(currentVersion, finalVersion);
+ }
+
+ /** Builds a Map of SchemaType and its version of given set of {@link AppSearchSchema}. */
+ @NonNull
+ public static Map<String, Integer> buildVersionMap(
+ @NonNull Collection<AppSearchSchema> schemas) {
+ Map<String, Integer> currentVersionMap = new ArrayMap<>(schemas.size());
+ for (AppSearchSchema currentSchema : schemas) {
+ currentVersionMap.put(currentSchema.getSchemaType(), currentSchema.getVersion());
+ }
+ return currentVersionMap;
+ }
+}
diff --git a/service/java/com/android/server/appsearch/AppSearchManagerService.java b/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 27c9ccb..77aa14c 100644
--- a/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -193,7 +193,7 @@
try {
// TODO(b/173451571): reduce burden of binder thread by enqueue request onto
// a separate thread.
- impl.putDocument(packageName, databaseName, document);
+ impl.putDocument(packageName, databaseName, document, /*logger=*/ null);
resultBuilder.setSuccess(document.getUri(), /*result=*/ null);
} catch (Throwable t) {
resultBuilder.setResult(document.getUri(), throwableToFailedResult(t));
diff --git a/service/java/com/android/server/appsearch/VisibilityStore.java b/service/java/com/android/server/appsearch/VisibilityStore.java
index 7c92456..ad94a0a 100644
--- a/service/java/com/android/server/appsearch/VisibilityStore.java
+++ b/service/java/com/android/server/appsearch/VisibilityStore.java
@@ -368,7 +368,8 @@
packageAccessibleDocuments.toArray(new GenericDocument[0]));
}
- mAppSearchImpl.putDocument(PACKAGE_NAME, DATABASE_NAME, visibilityDocument.build());
+ mAppSearchImpl.putDocument(
+ PACKAGE_NAME, DATABASE_NAME, visibilityDocument.build(), /*logger=*/ null);
// Update derived data structures.
mNotPlatformSurfaceableMap.put(prefix, schemasNotPlatformSurfaceable);
diff --git a/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index e2c211b..5e8760e 100644
--- a/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -17,6 +17,7 @@
package com.android.server.appsearch.external.localstorage;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.WorkerThread;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSchema;
@@ -29,6 +30,7 @@
import android.app.appsearch.exceptions.AppSearchException;
import android.content.Context;
import android.os.Bundle;
+import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -43,6 +45,7 @@
import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.SetSchemaResponseToProtoConverter;
import com.android.server.appsearch.external.localstorage.converter.TypePropertyPathToProtoConverter;
+import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
import com.google.android.icing.IcingSearchEngine;
import com.google.android.icing.proto.DeleteByQueryResultProto;
@@ -449,23 +452,65 @@
public void putDocument(
@NonNull String packageName,
@NonNull String databaseName,
- @NonNull GenericDocument document)
+ @NonNull GenericDocument document,
+ @Nullable AppSearchLogger logger)
throws AppSearchException {
+ PutDocumentStats.Builder pStatsBuilder = null;
+ if (logger != null) {
+ pStatsBuilder = new PutDocumentStats.Builder(packageName, databaseName);
+ }
+ long totalStartTimeMillis = SystemClock.elapsedRealtime();
+
mReadWriteLock.writeLock().lock();
try {
throwIfClosedLocked();
+ // Generate Document Proto
+ long generateDocumentProtoStartTimeMillis = SystemClock.elapsedRealtime();
DocumentProto.Builder documentBuilder =
GenericDocumentToProtoConverter.toDocumentProto(document).toBuilder();
+ long generateDocumentProtoEndTimeMillis = SystemClock.elapsedRealtime();
+
+ // Rewrite Document Type
+ long rewriteDocumentTypeStartTimeMillis = SystemClock.elapsedRealtime();
String prefix = createPrefix(packageName, databaseName);
addPrefixToDocument(documentBuilder, prefix);
+ long rewriteDocumentTypeEndTimeMillis = SystemClock.elapsedRealtime();
PutResultProto putResultProto = mIcingSearchEngineLocked.put(documentBuilder.build());
addToMap(mNamespaceMapLocked, prefix, documentBuilder.getNamespace());
+ // Logging stats
+ if (logger != null) {
+ pStatsBuilder
+ .getGeneralStatsBuilder()
+ .setStatusCode(
+ statusProtoToAppSearchException(putResultProto.getStatus())
+ .getResultCode());
+ pStatsBuilder
+ .setGenerateDocumentProtoLatencyMillis(
+ (int)
+ (generateDocumentProtoEndTimeMillis
+ - generateDocumentProtoStartTimeMillis))
+ .setRewriteDocumentTypesLatencyMillis(
+ (int)
+ (rewriteDocumentTypeEndTimeMillis
+ - rewriteDocumentTypeStartTimeMillis));
+ AppSearchLoggerHelper.copyNativeStats(
+ putResultProto.getPutDocumentStats(), pStatsBuilder);
+ }
+
checkSuccess(putResultProto.getStatus());
} finally {
mReadWriteLock.writeLock().unlock();
+
+ if (logger != null) {
+ long totalEndTimeMillis = SystemClock.elapsedRealtime();
+ pStatsBuilder
+ .getGeneralStatsBuilder()
+ .setTotalLatencyMillis((int) (totalEndTimeMillis - totalStartTimeMillis));
+ logger.logStats(pStatsBuilder.build());
+ }
}
}
diff --git a/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java b/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
new file mode 100644
index 0000000..5680670
--- /dev/null
+++ b/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
@@ -0,0 +1,58 @@
+/*
+ * 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;
+
+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;
+
+/**
+ * Class contains helper functions for logging.
+ *
+ * <p>E.g. we need to have helper functions to copy numbers from IcingLib to stats classes.
+ *
+ * @hide
+ */
+public final class AppSearchLoggerHelper {
+ private AppSearchLoggerHelper() {}
+
+ /**
+ * Copies native stats to builder.
+ *
+ * @param fromNativeStats stats copied from
+ * @param toStatsBuilder stats copied to
+ */
+ static void copyNativeStats(
+ @NonNull PutDocumentStatsProto fromNativeStats,
+ @NonNull PutDocumentStats.Builder toStatsBuilder) {
+ Preconditions.checkNotNull(fromNativeStats);
+ Preconditions.checkNotNull(toStatsBuilder);
+ toStatsBuilder
+ .setNativeLatencyMillis(fromNativeStats.getLatencyMs())
+ .setNativeDocumentStoreLatencyMillis(fromNativeStats.getDocumentStoreLatencyMs())
+ .setNativeIndexLatencyMillis(fromNativeStats.getIndexLatencyMs())
+ .setNativeIndexMergeLatencyMillis(fromNativeStats.getIndexMergeLatencyMs())
+ .setNativeDocumentSizeBytes(fromNativeStats.getDocumentSize())
+ .setNativeNumTokensIndexed(
+ fromNativeStats.getTokenizationStats().getNumTokensIndexed())
+ .setNativeExceededMaxNumTokens(
+ fromNativeStats.getTokenizationStats().getExceededMaxTokenNum());
+ }
+}
diff --git a/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java b/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java
deleted file mode 100644
index 4b8ce6d..0000000
--- a/service/java/com/android/server/appsearch/external/localstorage/AppSearchMigrationHelperImpl.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * 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;
-
-import static android.app.appsearch.AppSearchResult.throwableToFailedResult;
-
-import android.annotation.NonNull;
-import android.app.appsearch.AppSearchBatchResult;
-import android.app.appsearch.AppSearchMigrationHelper;
-import android.app.appsearch.GenericDocument;
-import android.app.appsearch.SearchResultPage;
-import android.app.appsearch.SearchSpec;
-import android.app.appsearch.SetSchemaResponse;
-import android.app.appsearch.exceptions.AppSearchException;
-import android.os.Bundle;
-import android.os.Parcel;
-
-import com.android.internal.util.Preconditions;
-
-import com.google.protobuf.CodedInputStream;
-import com.google.protobuf.CodedOutputStream;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Map;
-
-/**
- * An implementation of {@link AppSearchMigrationHelper} which query document and save post-migrated
- * documents to locally in the app's storage space.
- */
-class AppSearchMigrationHelperImpl implements AppSearchMigrationHelper {
- private final AppSearchImpl mAppSearchImpl;
- private final String mPackageName;
- private final String mDatabaseName;
- private final File mFile;
- private final Map<String, Integer> mCurrentVersionMap;
- private final Map<String, Integer> mFinalVersionMap;
-
- AppSearchMigrationHelperImpl(
- @NonNull AppSearchImpl appSearchImpl,
- @NonNull Map<String, Integer> currentVersionMap,
- @NonNull Map<String, Integer> finalVersionMap,
- @NonNull String packageName,
- @NonNull String databaseName)
- throws IOException {
- mAppSearchImpl = Preconditions.checkNotNull(appSearchImpl);
- mCurrentVersionMap = Preconditions.checkNotNull(currentVersionMap);
- mFinalVersionMap = Preconditions.checkNotNull(finalVersionMap);
- mPackageName = Preconditions.checkNotNull(packageName);
- mDatabaseName = Preconditions.checkNotNull(databaseName);
- mFile = File.createTempFile(/*prefix=*/ "appsearch", /*suffix=*/ null);
- }
-
- @Override
- public void queryAndTransform(
- @NonNull String schemaType, @NonNull AppSearchMigrationHelper.Transformer migrator)
- throws Exception {
- Preconditions.checkState(mFile.exists(), "Internal temp file does not exist.");
- int currentVersion = mCurrentVersionMap.get(schemaType);
- int finalVersion = mFinalVersionMap.get(schemaType);
- try (FileOutputStream outputStream = new FileOutputStream(mFile)) {
- // TODO(b/151178558) change the output stream so that we can use it in platform
- CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputStream);
- SearchResultPage searchResultPage =
- mAppSearchImpl.query(
- mPackageName,
- mDatabaseName,
- /*queryExpression=*/ "",
- new SearchSpec.Builder()
- .addFilterSchemas(schemaType)
- .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
- .build());
- while (!searchResultPage.getResults().isEmpty()) {
- for (int i = 0; i < searchResultPage.getResults().size(); i++) {
- GenericDocument newDocument =
- migrator.transform(
- currentVersion,
- finalVersion,
- searchResultPage.getResults().get(i).getGenericDocument());
- Bundle bundle = newDocument.getBundle();
- Parcel parcel = Parcel.obtain();
- parcel.writeBundle(bundle);
- byte[] serializedMessage = parcel.marshall();
- parcel.recycle();
- codedOutputStream.writeByteArrayNoTag(serializedMessage);
- }
- codedOutputStream.flush();
- searchResultPage = mAppSearchImpl.getNextPage(searchResultPage.getNextPageToken());
- outputStream.flush();
- }
- }
- }
-
- /**
- * Reads {@link GenericDocument} from the temperate file and saves them to AppSearch.
- *
- * <p>This method should be only called once.
- *
- * @return the {@link AppSearchBatchResult} for migration documents.
- */
- @NonNull
- public SetSchemaResponse readAndPutDocuments(SetSchemaResponse.Builder responseBuilder)
- throws IOException, AppSearchException {
- Preconditions.checkState(mFile.exists(), "Internal temp file does not exist.");
- try (InputStream inputStream = new FileInputStream(mFile)) {
- CodedInputStream codedInputStream = CodedInputStream.newInstance(inputStream);
- while (!codedInputStream.isAtEnd()) {
- GenericDocument document = readDocumentFromInputStream(codedInputStream);
- try {
- mAppSearchImpl.putDocument(mPackageName, mDatabaseName, document);
- } catch (Throwable t) {
- responseBuilder.addMigrationFailure(
- new SetSchemaResponse.MigrationFailure.Builder()
- .setNamespace(document.getNamespace())
- .setSchemaType(document.getSchemaType())
- .setUri(document.getUri())
- .setAppSearchResult(throwableToFailedResult(t))
- .build());
- }
- }
- mAppSearchImpl.persistToDisk();
- return responseBuilder.build();
- } finally {
- mFile.delete();
- }
- }
-
- void deleteTempFile() {
- mFile.delete();
- }
-
- /**
- * Reads {@link GenericDocument} from given {@link CodedInputStream}.
- *
- * @param codedInputStream The codedInputStream to read from
- * @throws IOException on File operation error.
- */
- @NonNull
- private static GenericDocument readDocumentFromInputStream(
- @NonNull CodedInputStream codedInputStream) throws IOException {
- byte[] serializedMessage = codedInputStream.readByteArray();
-
- Parcel parcel = Parcel.obtain();
- parcel.unmarshall(serializedMessage, 0, serializedMessage.length);
- parcel.setDataPosition(0);
- Bundle bundle = parcel.readBundle();
- parcel.recycle();
-
- return new GenericDocument(bundle);
- }
-}
diff --git a/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java b/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
index 81a5067..a724f95 100644
--- a/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
+++ b/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java
@@ -76,7 +76,7 @@
CallStats(@NonNull Builder builder) {
Preconditions.checkNotNull(builder);
- mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStats);
+ mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStatsBuilder).build();
mCallType = builder.mCallType;
mEstimatedBinderLatencyMillis = builder.mEstimatedBinderLatencyMillis;
mNumOperationsSucceeded = builder.mNumOperationsSucceeded;
@@ -132,15 +132,23 @@
/** Builder for {@link CallStats}. */
public static class Builder {
- @NonNull final GeneralStats mGeneralStats;
+ @NonNull final GeneralStats.Builder mGeneralStatsBuilder;
@CallType int mCallType;
int mEstimatedBinderLatencyMillis;
int mNumOperationsSucceeded;
int mNumOperationsFailed;
- /** Builder takes {@link GeneralStats} to hold general stats. */
- public Builder(@NonNull GeneralStats generalStats) {
- mGeneralStats = Preconditions.checkNotNull(generalStats);
+ /** Builder takes {@link GeneralStats.Builder}. */
+ public Builder(@NonNull String packageName, @NonNull String database) {
+ Preconditions.checkNotNull(packageName);
+ Preconditions.checkNotNull(database);
+ mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database);
+ }
+
+ /** Returns {@link GeneralStats.Builder}. */
+ @NonNull
+ public GeneralStats.Builder getGeneralStatsBuilder() {
+ return mGeneralStatsBuilder;
}
/** Sets type of the call. */
diff --git a/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java b/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java
index d2a45d5..8ce8eda 100644
--- a/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java
+++ b/service/java/com/android/server/appsearch/external/localstorage/stats/GeneralStats.java
@@ -82,18 +82,18 @@
public static class Builder {
@NonNull final String mPackageName;
@NonNull final String mDatabase;
- @AppSearchResult.ResultCode int mStatusCode;
+ @AppSearchResult.ResultCode int mStatusCode = AppSearchResult.RESULT_UNKNOWN_ERROR;
int mTotalLatencyMillis;
/**
* Constructor
*
* @param packageName name of the package logging stats
- * @param dataBase name of the database logging stats
+ * @param database name of the database logging stats
*/
- public Builder(@NonNull String packageName, @NonNull String dataBase) {
+ public Builder(@NonNull String packageName, @NonNull String database) {
mPackageName = Preconditions.checkNotNull(packageName);
- mDatabase = Preconditions.checkNotNull(dataBase);
+ mDatabase = Preconditions.checkNotNull(database);
}
/** Sets status code returned from {@link AppSearchResult#getResultCode()} */
diff --git a/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java b/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java
index b1b643b..c1f6fb1 100644
--- a/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java
+++ b/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java
@@ -54,12 +54,14 @@
/** Number of tokens added to the index. */
private final int mNativeNumTokensIndexed;
- /** Number of tokens clipped for exceeding the max number. */
- private final int mNativeNumTokensClipped;
+ /**
+ * Whether the number of tokens to be indexed exceeded the max number of tokens per document.
+ */
+ private final boolean mNativeExceededMaxNumTokens;
PutDocumentStats(@NonNull Builder builder) {
Preconditions.checkNotNull(builder);
- mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStats);
+ mGeneralStats = Preconditions.checkNotNull(builder.mGeneralStatsBuilder).build();
mGenerateDocumentProtoLatencyMillis = builder.mGenerateDocumentProtoLatencyMillis;
mRewriteDocumentTypesLatencyMillis = builder.mRewriteDocumentTypesLatencyMillis;
mNativeLatencyMillis = builder.mNativeLatencyMillis;
@@ -68,7 +70,7 @@
mNativeIndexMergeLatencyMillis = builder.mNativeIndexMergeLatencyMillis;
mNativeDocumentSizeBytes = builder.mNativeDocumentSizeBytes;
mNativeNumTokensIndexed = builder.mNativeNumTokensIndexed;
- mNativeNumTokensClipped = builder.mNativeNumTokensClipped;
+ mNativeExceededMaxNumTokens = builder.mNativeExceededMaxNumTokens;
}
/** Returns the {@link GeneralStats} object attached to this instance. */
@@ -117,14 +119,17 @@
return mNativeNumTokensIndexed;
}
- /** Returns number of tokens clipped for exceeding the max number. */
- public int getNativeNumTokensClipped() {
- return mNativeNumTokensClipped;
+ /**
+ * Returns whether the number of tokens to be indexed exceeded the max number of tokens per
+ * document.
+ */
+ public boolean getNativeExceededMaxNumTokens() {
+ return mNativeExceededMaxNumTokens;
}
/** Builder for {@link PutDocumentStats}. */
public static class Builder {
- @NonNull final GeneralStats mGeneralStats;
+ @NonNull final GeneralStats.Builder mGeneralStatsBuilder;
int mGenerateDocumentProtoLatencyMillis;
int mRewriteDocumentTypesLatencyMillis;
int mNativeLatencyMillis;
@@ -133,11 +138,19 @@
int mNativeIndexMergeLatencyMillis;
int mNativeDocumentSizeBytes;
int mNativeNumTokensIndexed;
- int mNativeNumTokensClipped;
+ boolean mNativeExceededMaxNumTokens;
- /** Builder takes {@link GeneralStats} to hold general stats. */
- public Builder(@NonNull GeneralStats generalStats) {
- mGeneralStats = Preconditions.checkNotNull(generalStats);
+ /** Builder takes {@link GeneralStats.Builder}. */
+ public Builder(@NonNull String packageName, @NonNull String database) {
+ Preconditions.checkNotNull(packageName);
+ Preconditions.checkNotNull(database);
+ mGeneralStatsBuilder = new GeneralStats.Builder(packageName, database);
+ }
+
+ /** Returns {@link GeneralStats.Builder}. */
+ @NonNull
+ public GeneralStats.Builder getGeneralStatsBuilder() {
+ return mGeneralStatsBuilder;
}
/** Sets how much time we spend for generating document proto, in milliseconds. */
@@ -200,10 +213,13 @@
return this;
}
- /** Sets number of tokens clipped for exceeding the max number. */
+ /**
+ * Sets whether the number of tokens to be indexed exceeded the max number of tokens per
+ * document.
+ */
@NonNull
- public Builder setNativeNumTokensClipped(int nativeNumTokensClipped) {
- mNativeNumTokensClipped = nativeNumTokensClipped;
+ public Builder setNativeExceededMaxNumTokens(boolean nativeExceededMaxNumTokens) {
+ mNativeExceededMaxNumTokens = nativeExceededMaxNumTokens;
return this;
}
diff --git a/synced_jetpack_changeid.txt b/synced_jetpack_changeid.txt
index 68531b6..0952215 100644
--- a/synced_jetpack_changeid.txt
+++ b/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I1926fb1d13628607f7a513c8149b65dd86c98dd6
+I723a9d7b5e64329ab25b6d7627f3b2d222c31ac7
diff --git a/testing/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/testing/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
index b5f4912..ad22cba 100644
--- a/testing/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ b/testing/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
@@ -103,12 +103,12 @@
// Insert package1 document
GenericDocument document1 =
new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
- mAppSearchImpl.putDocument("package1", "database1", document1);
+ mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
// Insert package2 document
GenericDocument document2 =
new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
- mAppSearchImpl.putDocument("package2", "database2", document2);
+ mAppSearchImpl.putDocument("package2", "database2", document2, /*logger=*/ null);
// No query filters specified, global query can retrieve all documents.
SearchSpec searchSpec =
@@ -155,12 +155,12 @@
// Insert package1 document
GenericDocument document1 =
new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
- mAppSearchImpl.putDocument("package1", "database1", document1);
+ mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null);
// Insert package2 document
GenericDocument document2 =
new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
- mAppSearchImpl.putDocument("package2", "database2", document2);
+ mAppSearchImpl.putDocument("package2", "database2", document2, /*logger=*/ null);
// "package1" filter specified
SearchSpec searchSpec =
diff --git a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index c84c1cf..e0cdedd 100644
--- a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -416,7 +416,7 @@
i++) {
GenericDocument document =
new GenericDocument.Builder<>("namespace", "uri" + i, "type").build();
- mAppSearchImpl.putDocument("package", "database", document);
+ mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null);
}
// Check optimize() will release 0 docs since there is no deletion.
@@ -476,7 +476,7 @@
// Insert document
GenericDocument document =
new GenericDocument.Builder<>("namespace", "uri", "type").build();
- mAppSearchImpl.putDocument("package", "database", document);
+ mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null);
// Rewrite SearchSpec
mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
@@ -516,11 +516,11 @@
// Insert documents
GenericDocument document1 =
new GenericDocument.Builder<>("namespace", "uri", "typeA").build();
- mAppSearchImpl.putDocument("package", "database1", document1);
+ mAppSearchImpl.putDocument("package", "database1", document1, /*logger=*/ null);
GenericDocument document2 =
new GenericDocument.Builder<>("namespace", "uri", "typeB").build();
- mAppSearchImpl.putDocument("package", "database2", document2);
+ mAppSearchImpl.putDocument("package", "database2", document2, /*logger=*/ null);
// Rewrite SearchSpec
mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(
@@ -560,7 +560,7 @@
// Insert document
GenericDocument document =
new GenericDocument.Builder<>("namespace", "uri", "type").build();
- mAppSearchImpl.putDocument("package", "database", document);
+ mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null);
// If 'allowedPrefixedSchemas' is empty, this returns false since there's nothing to
// search over. Despite the searchSpecProto having schema type filters.
@@ -613,7 +613,7 @@
// Insert package1 document
GenericDocument document =
new GenericDocument.Builder<>("namespace", "uri", "schema1").build();
- mAppSearchImpl.putDocument("package1", "database1", document);
+ mAppSearchImpl.putDocument("package1", "database1", document, /*logger=*/ null);
// No query filters specified, package2 shouldn't be able to query for package1's documents.
SearchSpec searchSpec =
@@ -624,7 +624,7 @@
// Insert package2 document
document = new GenericDocument.Builder<>("namespace", "uri", "schema2").build();
- mAppSearchImpl.putDocument("package2", "database2", document);
+ mAppSearchImpl.putDocument("package2", "database2", document, /*logger=*/ null);
// No query filters specified. package2 should only get its own documents back.
searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
@@ -663,7 +663,7 @@
// Insert package1 document
GenericDocument document =
new GenericDocument.Builder<>("namespace", "uri", "schema1").build();
- mAppSearchImpl.putDocument("package1", "database1", document);
+ mAppSearchImpl.putDocument("package1", "database1", document, /*logger=*/ null);
// "package1" filter specified, but package2 shouldn't be able to query for package1's
// documents.
@@ -678,7 +678,7 @@
// Insert package2 document
document = new GenericDocument.Builder<>("namespace", "uri", "schema2").build();
- mAppSearchImpl.putDocument("package2", "database2", document);
+ mAppSearchImpl.putDocument("package2", "database2", document, /*logger=*/ null);
// "package2" filter specified, package2 should only get its own documents back.
searchSpec =
@@ -1124,7 +1124,8 @@
appSearchImpl.putDocument(
"package",
"database",
- new GenericDocument.Builder<>("namespace", "uri", "type").build());
+ new GenericDocument.Builder<>("namespace", "uri", "type").build(),
+ /*logger=*/ null);
});
expectThrows(
diff --git a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
new file mode 100644
index 0000000..467ede4
--- /dev/null
+++ b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.appsearch.AppSearchResult;
+import android.app.appsearch.AppSearchSchema;
+import android.app.appsearch.GenericDocument;
+import android.content.Context;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.server.appsearch.external.localstorage.stats.CallStats;
+import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats;
+import com.android.server.appsearch.proto.PutDocumentStatsProto;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.util.Collections;
+import java.util.List;
+
+public class AppSearchLoggerTest {
+ @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+ private AppSearchImpl mAppSearchImpl;
+ private TestLogger mLogger;
+
+ @Before
+ public void setUp() throws Exception {
+ Context context = ApplicationProvider.getApplicationContext();
+
+ // Give ourselves global query permissions
+ mAppSearchImpl =
+ AppSearchImpl.create(
+ mTemporaryFolder.newFolder(),
+ context,
+ VisibilityStore.NO_OP_USER_ID,
+ /*globalQuerierPackage=*/ context.getPackageName());
+ mLogger = new TestLogger();
+ }
+
+ // Test only not thread safe.
+ public class TestLogger implements AppSearchLogger {
+ @Nullable PutDocumentStats mPutDocumentStats;
+
+ @Override
+ public void logStats(@NonNull CallStats stats) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void logStats(@NonNull PutDocumentStats stats) {
+ mPutDocumentStats = stats;
+ }
+ }
+
+ @Test
+ public void testAppSearchLoggerHelper_testCopyNativeStats_putDocument() {
+ final int nativeLatencyMillis = 3;
+ final int nativeDocumentStoreLatencyMillis = 4;
+ final int nativeIndexLatencyMillis = 5;
+ final int nativeIndexMergeLatencyMillis = 6;
+ final int nativeDocumentSize = 7;
+ final int nativeNumTokensIndexed = 8;
+ final boolean nativeExceededMaxNumTokens = true;
+ PutDocumentStatsProto nativePutDocumentStats =
+ PutDocumentStatsProto.newBuilder()
+ .setLatencyMs(nativeLatencyMillis)
+ .setDocumentStoreLatencyMs(nativeDocumentStoreLatencyMillis)
+ .setIndexLatencyMs(nativeIndexLatencyMillis)
+ .setIndexMergeLatencyMs(nativeIndexMergeLatencyMillis)
+ .setDocumentSize(nativeDocumentSize)
+ .setTokenizationStats(
+ PutDocumentStatsProto.TokenizationStats.newBuilder()
+ .setNumTokensIndexed(nativeNumTokensIndexed)
+ .setExceededMaxTokenNum(nativeExceededMaxNumTokens)
+ .build())
+ .build();
+ PutDocumentStats.Builder pBuilder = new PutDocumentStats.Builder("packageName", "database");
+
+ AppSearchLoggerHelper.copyNativeStats(nativePutDocumentStats, pBuilder);
+
+ PutDocumentStats pStats = pBuilder.build();
+ assertThat(pStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis);
+ assertThat(pStats.getNativeDocumentStoreLatencyMillis())
+ .isEqualTo(nativeDocumentStoreLatencyMillis);
+ assertThat(pStats.getNativeIndexLatencyMillis()).isEqualTo(nativeIndexLatencyMillis);
+ assertThat(pStats.getNativeIndexMergeLatencyMillis())
+ .isEqualTo(nativeIndexMergeLatencyMillis);
+ assertThat(pStats.getNativeDocumentSizeBytes()).isEqualTo(nativeDocumentSize);
+ assertThat(pStats.getNativeNumTokensIndexed()).isEqualTo(nativeNumTokensIndexed);
+ assertThat(pStats.getNativeExceededMaxNumTokens()).isEqualTo(nativeExceededMaxNumTokens);
+ }
+
+ //
+ // Testing actual logging
+ //
+ @Test
+ public void testLoggingStats_putDocument() throws Exception {
+ // Insert schema
+ final String testPackageName = "testPackage";
+ final String testDatabase = "testDatabase";
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ mAppSearchImpl.setSchema(
+ testPackageName,
+ testDatabase,
+ schemas,
+ /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
+ /*schemasPackageAccessible=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false);
+ GenericDocument document =
+ new GenericDocument.Builder<>("namespace", "uri", "type").build();
+
+ mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger);
+
+ PutDocumentStats pStats = mLogger.mPutDocumentStats;
+ assertThat(pStats).isNotNull();
+ assertThat(pStats.getGeneralStats().getPackageName()).isEqualTo(testPackageName);
+ assertThat(pStats.getGeneralStats().getDatabase()).isEqualTo(testDatabase);
+ assertThat(pStats.getGeneralStats().getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
+ // The rest of native stats have been tested in testCopyNativeStats
+ assertThat(pStats.getNativeDocumentSizeBytes()).isGreaterThan(0);
+ }
+}
diff --git a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
index 4308885..8dbf249 100644
--- a/testing/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
+++ b/testing/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
@@ -18,12 +18,14 @@
import static com.google.common.truth.Truth.assertThat;
+import android.app.appsearch.AppSearchResult;
+
import org.junit.Test;
public class AppSearchStatsTest {
static final String TEST_PACKAGE_NAME = "com.google.test";
static final String TEST_DATA_BASE = "testDataBase";
- static final int TEST_STATUS_CODE = 2;
+ static final int TEST_STATUS_CODE = AppSearchResult.RESULT_INTERNAL_ERROR;
static final int TEST_TOTAL_LATENCY_MILLIS = 20;
@Test
@@ -40,25 +42,38 @@
assertThat(gStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
}
+ /** Make sure status code is UNKNOWN if not set in {@link GeneralStats} */
+ @Test
+ public void testAppSearchStats_GeneralStats_defaultStatsCode_Unknown() {
+ final GeneralStats gStats =
+ new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
+ .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
+ .build();
+
+ assertThat(gStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+ assertThat(gStats.getDatabase()).isEqualTo(TEST_DATA_BASE);
+ assertThat(gStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_UNKNOWN_ERROR);
+ assertThat(gStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS);
+ }
+
@Test
public void testAppSearchStats_CallStats() {
final int estimatedBinderLatencyMillis = 1;
final int numOperationsSucceeded = 2;
final int numOperationsFailed = 3;
-
- final GeneralStats gStats =
- new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
- .setStatusCode(TEST_STATUS_CODE)
- .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
- .build();
final @CallStats.CallType int callType = CallStats.CALL_TYPE_PUT_DOCUMENTS;
- final CallStats cStats =
- new CallStats.Builder(gStats)
+
+ final CallStats.Builder cStatsBuilder =
+ new CallStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
.setCallType(callType)
.setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis)
.setNumOperationsSucceeded(numOperationsSucceeded)
- .setNumOperationsFailed(numOperationsFailed)
- .build();
+ .setNumOperationsFailed(numOperationsFailed);
+ cStatsBuilder
+ .getGeneralStatsBuilder()
+ .setStatusCode(TEST_STATUS_CODE)
+ .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS);
+ final CallStats cStats = cStatsBuilder.build();
assertThat(cStats.getGeneralStats().getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
assertThat(cStats.getGeneralStats().getDatabase()).isEqualTo(TEST_DATA_BASE);
@@ -82,15 +97,9 @@
final int nativeIndexMergeLatencyMillis = 6;
final int nativeDocumentSize = 7;
final int nativeNumTokensIndexed = 8;
- final int nativeNumTokensClipped = 9;
-
- final GeneralStats gStats =
- new GeneralStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
- .setStatusCode(TEST_STATUS_CODE)
- .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
- .build();
- final PutDocumentStats pStats =
- new PutDocumentStats.Builder(gStats)
+ final boolean nativeExceededMaxNumTokens = true;
+ final PutDocumentStats.Builder pStatsBuilder =
+ new PutDocumentStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
.setGenerateDocumentProtoLatencyMillis(generateDocumentProtoLatencyMillis)
.setRewriteDocumentTypesLatencyMillis(rewriteDocumentTypesLatencyMillis)
.setNativeLatencyMillis(nativeLatencyMillis)
@@ -99,8 +108,12 @@
.setNativeIndexMergeLatencyMillis(nativeIndexMergeLatencyMillis)
.setNativeDocumentSizeBytes(nativeDocumentSize)
.setNativeNumTokensIndexed(nativeNumTokensIndexed)
- .setNativeNumTokensClipped(nativeNumTokensClipped)
- .build();
+ .setNativeExceededMaxNumTokens(nativeExceededMaxNumTokens);
+ pStatsBuilder
+ .getGeneralStatsBuilder()
+ .setStatusCode(TEST_STATUS_CODE)
+ .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS);
+ final PutDocumentStats pStats = pStatsBuilder.build();
assertThat(pStats.getGeneralStats().getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
assertThat(pStats.getGeneralStats().getDatabase()).isEqualTo(TEST_DATA_BASE);
@@ -119,6 +132,6 @@
.isEqualTo(nativeIndexMergeLatencyMillis);
assertThat(pStats.getNativeDocumentSizeBytes()).isEqualTo(nativeDocumentSize);
assertThat(pStats.getNativeNumTokensIndexed()).isEqualTo(nativeNumTokensIndexed);
- assertThat(pStats.getNativeNumTokensClipped()).isEqualTo(nativeNumTokensClipped);
+ assertThat(pStats.getNativeExceededMaxNumTokens()).isEqualTo(nativeExceededMaxNumTokens);
}
}