Merge "Update framework from jetpack." into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
0f563404e9
@@ -19,6 +19,7 @@ import android.annotation.NonNull;
|
|||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -48,9 +49,9 @@ public final class AppSearchBatchResult<KeyType, ValueType> {
|
|||||||
@NonNull Map<KeyType, ValueType> successes,
|
@NonNull Map<KeyType, ValueType> successes,
|
||||||
@NonNull Map<KeyType, AppSearchResult<ValueType>> failures,
|
@NonNull Map<KeyType, AppSearchResult<ValueType>> failures,
|
||||||
@NonNull Map<KeyType, AppSearchResult<ValueType>> all) {
|
@NonNull Map<KeyType, AppSearchResult<ValueType>> all) {
|
||||||
mSuccesses = successes;
|
mSuccesses = Objects.requireNonNull(successes);
|
||||||
mFailures = failures;
|
mFailures = Objects.requireNonNull(failures);
|
||||||
mAll = all;
|
mAll = Objects.requireNonNull(all);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns {@code true} if this {@link AppSearchBatchResult} has no failures. */
|
/** Returns {@code true} if this {@link AppSearchBatchResult} has no failures. */
|
||||||
@@ -70,7 +71,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> {
|
|||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public Map<KeyType, ValueType> getSuccesses() {
|
public Map<KeyType, ValueType> getSuccesses() {
|
||||||
return mSuccesses;
|
return Collections.unmodifiableMap(mSuccesses);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,7 +82,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> {
|
|||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public Map<KeyType, AppSearchResult<ValueType>> getFailures() {
|
public Map<KeyType, AppSearchResult<ValueType>> getFailures() {
|
||||||
return mFailures;
|
return Collections.unmodifiableMap(mFailures);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -92,7 +93,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> {
|
|||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public Map<KeyType, AppSearchResult<ValueType>> getAll() {
|
public Map<KeyType, AppSearchResult<ValueType>> getAll() {
|
||||||
return mAll;
|
return Collections.unmodifiableMap(mAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,20 +129,37 @@ public final class AppSearchBatchResult<KeyType, ValueType> {
|
|||||||
* Associates the {@code key} with the provided successful return value.
|
* Associates the {@code key} with the provided successful return value.
|
||||||
*
|
*
|
||||||
* <p>Any previous mapping for a key, whether success or failure, is deleted.
|
* <p>Any previous mapping for a key, whether success or failure, is deleted.
|
||||||
|
*
|
||||||
|
* <p>This is a convenience function which is equivalent to {@code setResult(key,
|
||||||
|
* AppSearchResult.newSuccessfulResult(value))}.
|
||||||
|
*
|
||||||
|
* @param key The key to associate the result with; usually corresponds to some identifier
|
||||||
|
* from the input like an ID or name.
|
||||||
|
* @param value An optional value to associate with the successful result of the operation
|
||||||
|
* being performed.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("MissingGetterMatchingBuilder") // See getSuccesses
|
@SuppressWarnings("MissingGetterMatchingBuilder") // See getSuccesses
|
||||||
@NonNull
|
@NonNull
|
||||||
public Builder<KeyType, ValueType> setSuccess(
|
public Builder<KeyType, ValueType> setSuccess(
|
||||||
@NonNull KeyType key, @Nullable ValueType result) {
|
@NonNull KeyType key, @Nullable ValueType value) {
|
||||||
Objects.requireNonNull(key);
|
Objects.requireNonNull(key);
|
||||||
resetIfBuilt();
|
resetIfBuilt();
|
||||||
return setResult(key, AppSearchResult.newSuccessfulResult(result));
|
return setResult(key, AppSearchResult.newSuccessfulResult(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associates the {@code key} with the provided failure code and error message.
|
* Associates the {@code key} with the provided failure code and error message.
|
||||||
*
|
*
|
||||||
* <p>Any previous mapping for a key, whether success or failure, is deleted.
|
* <p>Any previous mapping for a key, whether success or failure, is deleted.
|
||||||
|
*
|
||||||
|
* <p>This is a convenience function which is equivalent to {@code setResult(key,
|
||||||
|
* AppSearchResult.newFailedResult(resultCode, errorMessage))}.
|
||||||
|
*
|
||||||
|
* @param key The key to associate the result with; usually corresponds to some identifier
|
||||||
|
* from the input like an ID or name.
|
||||||
|
* @param resultCode One of the constants documented in {@link
|
||||||
|
* AppSearchResult#getResultCode}.
|
||||||
|
* @param errorMessage An optional string describing the reason or nature of the failure.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("MissingGetterMatchingBuilder") // See getFailures
|
@SuppressWarnings("MissingGetterMatchingBuilder") // See getFailures
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -158,6 +176,10 @@ public final class AppSearchBatchResult<KeyType, ValueType> {
|
|||||||
* Associates the {@code key} with the provided {@code result}.
|
* Associates the {@code key} with the provided {@code result}.
|
||||||
*
|
*
|
||||||
* <p>Any previous mapping for a key, whether success or failure, is deleted.
|
* <p>Any previous mapping for a key, whether success or failure, is deleted.
|
||||||
|
*
|
||||||
|
* @param key The key to associate the result with; usually corresponds to some identifier
|
||||||
|
* from the input like an ID or name.
|
||||||
|
* @param result The result to associate with the key.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("MissingGetterMatchingBuilder") // See getAll
|
@SuppressWarnings("MissingGetterMatchingBuilder") // See getAll
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -183,8 +205,7 @@ public final class AppSearchBatchResult<KeyType, ValueType> {
|
|||||||
@NonNull
|
@NonNull
|
||||||
public AppSearchBatchResult<KeyType, ValueType> build() {
|
public AppSearchBatchResult<KeyType, ValueType> build() {
|
||||||
mBuilt = true;
|
mBuilt = true;
|
||||||
return new AppSearchBatchResult<>(
|
return new AppSearchBatchResult<>(mSuccesses, mFailures, mAll);
|
||||||
new ArrayMap<>(mSuccesses), new ArrayMap<>(mFailures), new ArrayMap<>(mAll));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetIfBuilt() {
|
private void resetIfBuilt() {
|
||||||
|
|||||||
@@ -175,14 +175,24 @@ public final class AppSearchResult<ValueType> {
|
|||||||
return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage;
|
return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a new successful {@link AppSearchResult}. */
|
/**
|
||||||
|
* Creates a new successful {@link AppSearchResult}.
|
||||||
|
*
|
||||||
|
* @param value An optional value to associate with the successful result of the operation being
|
||||||
|
* performed.
|
||||||
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public static <ValueType> AppSearchResult<ValueType> newSuccessfulResult(
|
public static <ValueType> AppSearchResult<ValueType> newSuccessfulResult(
|
||||||
@Nullable ValueType value) {
|
@Nullable ValueType value) {
|
||||||
return new AppSearchResult<>(RESULT_OK, value, /*errorMessage=*/ null);
|
return new AppSearchResult<>(RESULT_OK, value, /*errorMessage=*/ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a new failed {@link AppSearchResult}. */
|
/**
|
||||||
|
* Creates a new failed {@link AppSearchResult}.
|
||||||
|
*
|
||||||
|
* @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}.
|
||||||
|
* @param errorMessage An optional string describing the reason or nature of the failure.
|
||||||
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public static <ValueType> AppSearchResult<ValueType> newFailedResult(
|
public static <ValueType> AppSearchResult<ValueType> newFailedResult(
|
||||||
@ResultCode int resultCode, @Nullable String errorMessage) {
|
@ResultCode int resultCode, @Nullable String errorMessage) {
|
||||||
|
|||||||
@@ -164,8 +164,7 @@ public final class GetByDocumentIdRequest {
|
|||||||
@NonNull
|
@NonNull
|
||||||
public GetByDocumentIdRequest build() {
|
public GetByDocumentIdRequest build() {
|
||||||
mBuilt = true;
|
mBuilt = true;
|
||||||
return new GetByDocumentIdRequest(
|
return new GetByDocumentIdRequest(mNamespace, mIds, mProjectionTypePropertyPaths);
|
||||||
mNamespace, new ArraySet<>(mIds), new ArrayMap<>(mProjectionTypePropertyPaths));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetIfBuilt() {
|
private void resetIfBuilt() {
|
||||||
|
|||||||
@@ -93,14 +93,25 @@ public final class ReportSystemUsageRequest {
|
|||||||
private final String mDocumentId;
|
private final String mDocumentId;
|
||||||
private Long mUsageTimestampMillis;
|
private Long mUsageTimestampMillis;
|
||||||
|
|
||||||
/** Creates a {@link ReportSystemUsageRequest.Builder} instance. */
|
/**
|
||||||
|
* Creates a {@link ReportSystemUsageRequest.Builder} instance.
|
||||||
|
*
|
||||||
|
* @param packageName The package name of the app which owns the document that was used
|
||||||
|
* (e.g. from {@link SearchResult#getPackageName}).
|
||||||
|
* @param databaseName The database in which the document that was used resides (e.g. from
|
||||||
|
* {@link SearchResult#getDatabaseName}).
|
||||||
|
* @param namespace The namespace of the document that was used (e.g. from {@link
|
||||||
|
* GenericDocument#getNamespace}.
|
||||||
|
* @param documentId The ID of document that was used (e.g. from {@link
|
||||||
|
* GenericDocument#getId}.
|
||||||
|
*/
|
||||||
public Builder(
|
public Builder(
|
||||||
@NonNull String packageName,
|
@NonNull String packageName,
|
||||||
@NonNull String database,
|
@NonNull String databaseName,
|
||||||
@NonNull String namespace,
|
@NonNull String namespace,
|
||||||
@NonNull String documentId) {
|
@NonNull String documentId) {
|
||||||
mPackageName = Objects.requireNonNull(packageName);
|
mPackageName = Objects.requireNonNull(packageName);
|
||||||
mDatabase = Objects.requireNonNull(database);
|
mDatabase = Objects.requireNonNull(databaseName);
|
||||||
mNamespace = Objects.requireNonNull(namespace);
|
mNamespace = Objects.requireNonNull(namespace);
|
||||||
mDocumentId = Objects.requireNonNull(documentId);
|
mDocumentId = Objects.requireNonNull(documentId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,14 @@ public final class ReportUsageRequest {
|
|||||||
private String mDocumentId;
|
private String mDocumentId;
|
||||||
private Long mUsageTimestampMillis;
|
private Long mUsageTimestampMillis;
|
||||||
|
|
||||||
/** Creates a {@link ReportUsageRequest.Builder} instance. */
|
/**
|
||||||
|
* Creates a new {@link ReportUsageRequest.Builder} instance.
|
||||||
|
*
|
||||||
|
* @param namespace The namespace of the document that was used (e.g. from {@link
|
||||||
|
* GenericDocument#getNamespace}.
|
||||||
|
* @param documentId The ID of document that was used (e.g. from {@link
|
||||||
|
* GenericDocument#getId}.
|
||||||
|
*/
|
||||||
public Builder(@NonNull String namespace, @NonNull String documentId) {
|
public Builder(@NonNull String namespace, @NonNull String documentId) {
|
||||||
mNamespace = Objects.requireNonNull(namespace);
|
mNamespace = Objects.requireNonNull(namespace);
|
||||||
mDocumentId = Objects.requireNonNull(documentId);
|
mDocumentId = Objects.requireNonNull(documentId);
|
||||||
|
|||||||
@@ -375,5 +375,19 @@ public class SetSchemaResponse {
|
|||||||
mBundle.getInt(RESULT_CODE_FIELD),
|
mBundle.getInt(RESULT_CODE_FIELD),
|
||||||
mBundle.getString(ERROR_MESSAGE_FIELD, /*defaultValue=*/ ""));
|
mBundle.getString(ERROR_MESSAGE_FIELD, /*defaultValue=*/ ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "MigrationFailure { schemaType: "
|
||||||
|
+ getSchemaType()
|
||||||
|
+ ", namespace: "
|
||||||
|
+ getNamespace()
|
||||||
|
+ ", documentId: "
|
||||||
|
+ getDocumentId()
|
||||||
|
+ ", appSearchResult: "
|
||||||
|
+ getAppSearchResult().toString()
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
104
apex/appsearch/framework/java/external/android/app/appsearch/util/LogUtil.java
vendored
Normal file
104
apex/appsearch/framework/java/external/android/app/appsearch/util/LogUtil.java
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* 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.annotation.Nullable;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for logging to logcat.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public final class LogUtil {
|
||||||
|
/**
|
||||||
|
* The {@link #piiTrace} logs are intended for sensitive data that can't be enabled in
|
||||||
|
* production, so they are build-gated by this constant.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>0: no tracing.
|
||||||
|
* <li>1: fast tracing (statuses/counts only)
|
||||||
|
* <li>2: full tracing (complete messages)
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
private static final int PII_TRACE_LEVEL = 0;
|
||||||
|
|
||||||
|
private final String mTag;
|
||||||
|
|
||||||
|
public LogUtil(@NonNull String tag) {
|
||||||
|
mTag = Objects.requireNonNull(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns whether piiTrace() is enabled (PII_TRACE_LEVEL > 0). */
|
||||||
|
public boolean isPiiTraceEnabled() {
|
||||||
|
return PII_TRACE_LEVEL > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If icing lib interaction tracing is enabled via {@link #PII_TRACE_LEVEL}, logs the provided
|
||||||
|
* message to logcat.
|
||||||
|
*
|
||||||
|
* <p>If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately.
|
||||||
|
*/
|
||||||
|
public void piiTrace(@NonNull String message) {
|
||||||
|
piiTrace(message, /*fastTraceObj=*/ null, /*fullTraceObj=*/ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If icing lib interaction tracing is enabled via {@link #PII_TRACE_LEVEL}, logs the provided
|
||||||
|
* message and object to logcat.
|
||||||
|
*
|
||||||
|
* <p>If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately.
|
||||||
|
*
|
||||||
|
* <p>Otherwise, {@code traceObj} is logged if it is non-null.
|
||||||
|
*/
|
||||||
|
public void piiTrace(@NonNull String message, @Nullable Object traceObj) {
|
||||||
|
piiTrace(message, /*fastTraceObj=*/ traceObj, /*fullTraceObj=*/ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If icing lib interaction tracing is enabled via {@link #PII_TRACE_LEVEL}, logs the provided
|
||||||
|
* message and objects to logcat.
|
||||||
|
*
|
||||||
|
* <p>If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately.
|
||||||
|
*
|
||||||
|
* <p>If {@link #PII_TRACE_LEVEL} is 1, {@code fastTraceObj} is logged if it is non-null.
|
||||||
|
*
|
||||||
|
* <p>If {@link #PII_TRACE_LEVEL} is 2, {@code fullTraceObj} is logged if it is non-null, else
|
||||||
|
* {@code fastTraceObj} is logged if it is non-null..
|
||||||
|
*/
|
||||||
|
public void piiTrace(
|
||||||
|
@NonNull String message, @Nullable Object fastTraceObj, @Nullable Object fullTraceObj) {
|
||||||
|
if (PII_TRACE_LEVEL == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StringBuilder builder = new StringBuilder("(trace) ").append(message);
|
||||||
|
if (PII_TRACE_LEVEL == 1 && fastTraceObj != null) {
|
||||||
|
builder.append(": ").append(fastTraceObj);
|
||||||
|
} else if (PII_TRACE_LEVEL == 2 && fullTraceObj != null) {
|
||||||
|
builder.append(": ").append(fullTraceObj);
|
||||||
|
} else if (PII_TRACE_LEVEL == 2 && fastTraceObj != null) {
|
||||||
|
builder.append(": ").append(fastTraceObj);
|
||||||
|
}
|
||||||
|
Log.i(mTag, builder.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,7 @@ import static com.android.server.appsearch.external.localstorage.util.PrefixUtil
|
|||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.annotation.WorkerThread;
|
import android.annotation.WorkerThread;
|
||||||
|
import android.app.appsearch.AppSearchResult;
|
||||||
import android.app.appsearch.AppSearchSchema;
|
import android.app.appsearch.AppSearchSchema;
|
||||||
import android.app.appsearch.GenericDocument;
|
import android.app.appsearch.GenericDocument;
|
||||||
import android.app.appsearch.GetByDocumentIdRequest;
|
import android.app.appsearch.GetByDocumentIdRequest;
|
||||||
@@ -38,6 +39,7 @@ import android.app.appsearch.SearchSpec;
|
|||||||
import android.app.appsearch.SetSchemaResponse;
|
import android.app.appsearch.SetSchemaResponse;
|
||||||
import android.app.appsearch.StorageInfo;
|
import android.app.appsearch.StorageInfo;
|
||||||
import android.app.appsearch.exceptions.AppSearchException;
|
import android.app.appsearch.exceptions.AppSearchException;
|
||||||
|
import android.app.appsearch.util.LogUtil;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
@@ -147,8 +149,11 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
|
|
||||||
private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
|
private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
|
||||||
|
|
||||||
|
private final LogUtil mLogUtil = new LogUtil(TAG);
|
||||||
|
|
||||||
@GuardedBy("mReadWriteLock")
|
@GuardedBy("mReadWriteLock")
|
||||||
private final IcingSearchEngine mIcingSearchEngineLocked;
|
@VisibleForTesting
|
||||||
|
final IcingSearchEngine mIcingSearchEngineLocked;
|
||||||
|
|
||||||
@GuardedBy("mReadWriteLock")
|
@GuardedBy("mReadWriteLock")
|
||||||
private final VisibilityStore mVisibilityStoreLocked;
|
private final VisibilityStore mVisibilityStoreLocked;
|
||||||
@@ -216,7 +221,7 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
appSearchImpl.initializeVisibilityStore();
|
appSearchImpl.initializeVisibilityStore();
|
||||||
long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime();
|
long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime();
|
||||||
|
|
||||||
if (logger != null && initStatsBuilder != null) {
|
if (logger != null) {
|
||||||
initStatsBuilder
|
initStatsBuilder
|
||||||
.setTotalLatencyMillis(
|
.setTotalLatencyMillis(
|
||||||
(int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis))
|
(int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis))
|
||||||
@@ -247,47 +252,60 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
IcingSearchEngineOptions.newBuilder()
|
IcingSearchEngineOptions.newBuilder()
|
||||||
.setBaseDir(icingDir.getAbsolutePath())
|
.setBaseDir(icingDir.getAbsolutePath())
|
||||||
.build();
|
.build();
|
||||||
|
mLogUtil.piiTrace("Constructing IcingSearchEngine, request", options);
|
||||||
mIcingSearchEngineLocked = new IcingSearchEngine(options);
|
mIcingSearchEngineLocked = new IcingSearchEngine(options);
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"Constructing IcingSearchEngine, response",
|
||||||
|
Objects.hashCode(mIcingSearchEngineLocked));
|
||||||
|
|
||||||
mVisibilityStoreLocked =
|
mVisibilityStoreLocked =
|
||||||
new VisibilityStore(this, context, userId, globalQuerierPackage);
|
new VisibilityStore(this, context, userId, globalQuerierPackage);
|
||||||
|
|
||||||
|
// The core initialization procedure. If any part of this fails, we bail into
|
||||||
|
// resetLocked(), deleting all data (but hopefully allowing AppSearchImpl to come up).
|
||||||
|
try {
|
||||||
|
mLogUtil.piiTrace("icingSearchEngine.initialize, request");
|
||||||
InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
|
InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"icingSearchEngine.initialize, response",
|
||||||
|
initializeResultProto.getStatus(),
|
||||||
|
initializeResultProto);
|
||||||
|
|
||||||
if (initStatsBuilder != null) {
|
if (initStatsBuilder != null) {
|
||||||
initStatsBuilder
|
initStatsBuilder
|
||||||
.setStatusCode(
|
.setStatusCode(
|
||||||
statusProtoToAppSearchException(initializeResultProto.getStatus())
|
statusProtoToResultCode(initializeResultProto.getStatus()))
|
||||||
.getResultCode())
|
|
||||||
// TODO(b/173532925) how to get DeSyncs value
|
// TODO(b/173532925) how to get DeSyncs value
|
||||||
.setHasDeSync(false);
|
.setHasDeSync(false);
|
||||||
AppSearchLoggerHelper.copyNativeStats(
|
AppSearchLoggerHelper.copyNativeStats(
|
||||||
initializeResultProto.getInitializeStats(), initStatsBuilder);
|
initializeResultProto.getInitializeStats(), initStatsBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
long prepareSchemaAndNamespacesLatencyStartMillis = SystemClock.elapsedRealtime();
|
|
||||||
SchemaProto schemaProto;
|
|
||||||
GetAllNamespacesResultProto getAllNamespacesResultProto = null;
|
|
||||||
try {
|
|
||||||
checkSuccess(initializeResultProto.getStatus());
|
checkSuccess(initializeResultProto.getStatus());
|
||||||
schemaProto = getSchemaProtoLocked();
|
|
||||||
getAllNamespacesResultProto = mIcingSearchEngineLocked.getAllNamespaces();
|
long prepareSchemaAndNamespacesLatencyStartMillis = SystemClock.elapsedRealtime();
|
||||||
checkSuccess(getAllNamespacesResultProto.getStatus());
|
SchemaProto schemaProto = getSchemaProtoLocked();
|
||||||
} catch (AppSearchException e) {
|
|
||||||
Log.w(TAG, "Error initializing, resetting IcingSearchEngine.", e);
|
mLogUtil.piiTrace("init:getAllNamespaces, request");
|
||||||
if (initStatsBuilder != null && getAllNamespacesResultProto != null) {
|
GetAllNamespacesResultProto getAllNamespacesResultProto =
|
||||||
|
mIcingSearchEngineLocked.getAllNamespaces();
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"init:getAllNamespaces, response",
|
||||||
|
getAllNamespacesResultProto.getNamespacesCount(),
|
||||||
|
getAllNamespacesResultProto);
|
||||||
|
|
||||||
|
if (initStatsBuilder != null) {
|
||||||
initStatsBuilder
|
initStatsBuilder
|
||||||
.setStatusCode(
|
.setStatusCode(
|
||||||
statusProtoToAppSearchException(
|
statusProtoToResultCode(
|
||||||
getAllNamespacesResultProto.getStatus())
|
getAllNamespacesResultProto.getStatus()))
|
||||||
.getResultCode())
|
|
||||||
.setPrepareSchemaAndNamespacesLatencyMillis(
|
.setPrepareSchemaAndNamespacesLatencyMillis(
|
||||||
(int)
|
(int)
|
||||||
(SystemClock.elapsedRealtime()
|
(SystemClock.elapsedRealtime()
|
||||||
- prepareSchemaAndNamespacesLatencyStartMillis));
|
- prepareSchemaAndNamespacesLatencyStartMillis));
|
||||||
}
|
}
|
||||||
// Some error. Reset and see if it fixes it.
|
|
||||||
resetLocked();
|
checkSuccess(getAllNamespacesResultProto.getStatus());
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate schema map
|
// Populate schema map
|
||||||
for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) {
|
for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) {
|
||||||
@@ -307,6 +325,18 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
(SystemClock.elapsedRealtime()
|
(SystemClock.elapsedRealtime()
|
||||||
- prepareSchemaAndNamespacesLatencyStartMillis));
|
- prepareSchemaAndNamespacesLatencyStartMillis));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mLogUtil.piiTrace("Init completed successfully");
|
||||||
|
|
||||||
|
} catch (AppSearchException e) {
|
||||||
|
// Some error. Reset and see if it fixes it.
|
||||||
|
Log.e(TAG, "Error initializing, resetting IcingSearchEngine.", e);
|
||||||
|
if (initStatsBuilder != null) {
|
||||||
|
initStatsBuilder.setStatusCode(e.getResultCode());
|
||||||
|
}
|
||||||
|
resetLocked(initStatsBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
mReadWriteLock.writeLock().unlock();
|
mReadWriteLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
@@ -321,8 +351,9 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
mReadWriteLock.writeLock().lock();
|
mReadWriteLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
throwIfClosedLocked();
|
throwIfClosedLocked();
|
||||||
|
mLogUtil.piiTrace("Initializing VisibilityStore, request");
|
||||||
mVisibilityStoreLocked.initialize();
|
mVisibilityStoreLocked.initialize();
|
||||||
|
mLogUtil.piiTrace("Initializing VisibilityStore, response");
|
||||||
} finally {
|
} finally {
|
||||||
mReadWriteLock.writeLock().unlock();
|
mReadWriteLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
@@ -348,9 +379,10 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
if (mClosedLocked) {
|
if (mClosedLocked) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
persistToDisk(PersistType.Code.FULL);
|
persistToDisk(PersistType.Code.FULL);
|
||||||
|
mLogUtil.piiTrace("icingSearchEngine.close, request");
|
||||||
mIcingSearchEngineLocked.close();
|
mIcingSearchEngineLocked.close();
|
||||||
|
mLogUtil.piiTrace("icingSearchEngine.close, response");
|
||||||
mClosedLocked = true;
|
mClosedLocked = true;
|
||||||
} catch (AppSearchException e) {
|
} catch (AppSearchException e) {
|
||||||
Log.w(TAG, "Error when closing AppSearchImpl.", e);
|
Log.w(TAG, "Error when closing AppSearchImpl.", e);
|
||||||
@@ -410,9 +442,12 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
rewriteSchema(prefix, existingSchemaBuilder, newSchemaBuilder.build());
|
rewriteSchema(prefix, existingSchemaBuilder, newSchemaBuilder.build());
|
||||||
|
|
||||||
// Apply schema
|
// Apply schema
|
||||||
|
SchemaProto finalSchema = existingSchemaBuilder.build();
|
||||||
|
mLogUtil.piiTrace("setSchema, request", finalSchema.getTypesCount(), finalSchema);
|
||||||
SetSchemaResultProto setSchemaResultProto =
|
SetSchemaResultProto setSchemaResultProto =
|
||||||
mIcingSearchEngineLocked.setSchema(
|
mIcingSearchEngineLocked.setSchema(finalSchema, forceOverride);
|
||||||
existingSchemaBuilder.build(), forceOverride);
|
mLogUtil.piiTrace(
|
||||||
|
"setSchema, response", setSchemaResultProto.getStatus(), setSchemaResultProto);
|
||||||
|
|
||||||
// Determine whether it succeeded.
|
// Determine whether it succeeded.
|
||||||
try {
|
try {
|
||||||
@@ -544,11 +579,16 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
mReadWriteLock.readLock().lock();
|
mReadWriteLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
throwIfClosedLocked();
|
throwIfClosedLocked();
|
||||||
|
mLogUtil.piiTrace("getAllNamespaces, request");
|
||||||
// We can't just use mNamespaceMap here because we have no way to prune namespaces from
|
// We can't just use mNamespaceMap here because we have no way to prune namespaces from
|
||||||
// mNamespaceMap when they have no more documents (e.g. after setting schema to empty or
|
// mNamespaceMap when they have no more documents (e.g. after setting schema to empty or
|
||||||
// using deleteByQuery).
|
// using deleteByQuery).
|
||||||
GetAllNamespacesResultProto getAllNamespacesResultProto =
|
GetAllNamespacesResultProto getAllNamespacesResultProto =
|
||||||
mIcingSearchEngineLocked.getAllNamespaces();
|
mIcingSearchEngineLocked.getAllNamespaces();
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"getAllNamespaces, response",
|
||||||
|
getAllNamespacesResultProto.getNamespacesCount(),
|
||||||
|
getAllNamespacesResultProto);
|
||||||
checkSuccess(getAllNamespacesResultProto.getStatus());
|
checkSuccess(getAllNamespacesResultProto.getStatus());
|
||||||
String prefix = createPrefix(packageName, databaseName);
|
String prefix = createPrefix(packageName, databaseName);
|
||||||
List<String> results = new ArrayList<>();
|
List<String> results = new ArrayList<>();
|
||||||
@@ -601,17 +641,18 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
String prefix = createPrefix(packageName, databaseName);
|
String prefix = createPrefix(packageName, databaseName);
|
||||||
addPrefixToDocument(documentBuilder, prefix);
|
addPrefixToDocument(documentBuilder, prefix);
|
||||||
long rewriteDocumentTypeEndTimeMillis = SystemClock.elapsedRealtime();
|
long rewriteDocumentTypeEndTimeMillis = SystemClock.elapsedRealtime();
|
||||||
|
DocumentProto finalDocument = documentBuilder.build();
|
||||||
|
|
||||||
|
mLogUtil.piiTrace("putDocument, request", finalDocument.getUri(), finalDocument);
|
||||||
PutResultProto putResultProto = mIcingSearchEngineLocked.put(documentBuilder.build());
|
PutResultProto putResultProto = mIcingSearchEngineLocked.put(documentBuilder.build());
|
||||||
|
mLogUtil.piiTrace("putDocument, response", putResultProto.getStatus(), putResultProto);
|
||||||
addToMap(mNamespaceMapLocked, prefix, documentBuilder.getNamespace());
|
addToMap(mNamespaceMapLocked, prefix, documentBuilder.getNamespace());
|
||||||
|
|
||||||
// Logging stats
|
// Logging stats
|
||||||
if (logger != null && pStatsBuilder != null) {
|
if (pStatsBuilder != null) {
|
||||||
pStatsBuilder
|
pStatsBuilder
|
||||||
.getGeneralStatsBuilder()
|
.getGeneralStatsBuilder()
|
||||||
.setStatusCode(
|
.setStatusCode(statusProtoToResultCode(putResultProto.getStatus()));
|
||||||
statusProtoToAppSearchException(putResultProto.getStatus())
|
|
||||||
.getResultCode());
|
|
||||||
pStatsBuilder
|
pStatsBuilder
|
||||||
.setGenerateDocumentProtoLatencyMillis(
|
.setGenerateDocumentProtoLatencyMillis(
|
||||||
(int)
|
(int)
|
||||||
@@ -629,7 +670,7 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
} finally {
|
} finally {
|
||||||
mReadWriteLock.writeLock().unlock();
|
mReadWriteLock.writeLock().unlock();
|
||||||
|
|
||||||
if (logger != null && pStatsBuilder != null) {
|
if (logger != null) {
|
||||||
long totalEndTimeMillis = SystemClock.elapsedRealtime();
|
long totalEndTimeMillis = SystemClock.elapsedRealtime();
|
||||||
pStatsBuilder
|
pStatsBuilder
|
||||||
.getGeneralStatsBuilder()
|
.getGeneralStatsBuilder()
|
||||||
@@ -685,8 +726,14 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
.addAllTypePropertyMasks(prefixedPropertyMasks)
|
.addAllTypePropertyMasks(prefixedPropertyMasks)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
String finalNamespace = createPrefix(packageName, databaseName) + namespace;
|
||||||
|
if (mLogUtil.isPiiTraceEnabled()) {
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"getDocument, request", finalNamespace + ", " + id + "," + getResultSpec);
|
||||||
|
}
|
||||||
GetResultProto getResultProto =
|
GetResultProto getResultProto =
|
||||||
mIcingSearchEngineLocked.get(prefix + namespace, id, getResultSpec);
|
mIcingSearchEngineLocked.get(finalNamespace, id, getResultSpec);
|
||||||
|
mLogUtil.piiTrace("getDocument, response", getResultProto.getStatus(), getResultProto);
|
||||||
checkSuccess(getResultProto.getStatus());
|
checkSuccess(getResultProto.getStatus());
|
||||||
|
|
||||||
// The schema type map cannot be null at this point. It could only be null if no
|
// The schema type map cannot be null at this point. It could only be null if no
|
||||||
@@ -754,7 +801,7 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
sStatsBuilder);
|
sStatsBuilder);
|
||||||
} finally {
|
} finally {
|
||||||
mReadWriteLock.readLock().unlock();
|
mReadWriteLock.readLock().unlock();
|
||||||
if (logger != null && sStatsBuilder != null) {
|
if (logger != null) {
|
||||||
sStatsBuilder.setTotalLatencyMillis(
|
sStatsBuilder.setTotalLatencyMillis(
|
||||||
(int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
|
(int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
|
||||||
logger.logStats(sStatsBuilder.build());
|
logger.logStats(sStatsBuilder.build());
|
||||||
@@ -832,9 +879,10 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Client didn't specify certain schemas to search over, check all schemas
|
// Client didn't specify certain schemas to search over, check all schemas
|
||||||
Set<String> prefixedSchemas = mSchemaMapLocked.get(prefix).keySet();
|
Map<String, SchemaTypeConfigProto> prefixedSchemas =
|
||||||
|
mSchemaMapLocked.get(prefix);
|
||||||
if (prefixedSchemas != null) {
|
if (prefixedSchemas != null) {
|
||||||
for (String prefixedSchema : prefixedSchemas) {
|
for (String prefixedSchema : prefixedSchemas.keySet()) {
|
||||||
if (packageName.equals(callerPackageName)
|
if (packageName.equals(callerPackageName)
|
||||||
|| mVisibilityStoreLocked.isSchemaSearchableByCaller(
|
|| mVisibilityStoreLocked.isSchemaSearchableByCaller(
|
||||||
prefix, prefixedSchema, callerUid)) {
|
prefix, prefixedSchema, callerUid)) {
|
||||||
@@ -854,7 +902,7 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
} finally {
|
} finally {
|
||||||
mReadWriteLock.readLock().unlock();
|
mReadWriteLock.readLock().unlock();
|
||||||
|
|
||||||
if (logger != null && sStatsBuilder != null) {
|
if (logger != null) {
|
||||||
sStatsBuilder.setTotalLatencyMillis(
|
sStatsBuilder.setTotalLatencyMillis(
|
||||||
(int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
|
(int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
|
||||||
logger.logStats(sStatsBuilder.build());
|
logger.logStats(sStatsBuilder.build());
|
||||||
@@ -938,18 +986,25 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
|
|
||||||
rewriteResultSpecForPrefixesLocked(resultSpecBuilder, prefixes, allowedPrefixedSchemas);
|
rewriteResultSpecForPrefixesLocked(resultSpecBuilder, prefixes, allowedPrefixedSchemas);
|
||||||
ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
|
ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
|
||||||
|
SearchSpecProto finalSearchSpec = searchSpecBuilder.build();
|
||||||
|
ResultSpecProto finalResultSpec = resultSpecBuilder.build();
|
||||||
|
|
||||||
long rewriteSearchSpecLatencyEndMillis = SystemClock.elapsedRealtime();
|
long rewriteSearchSpecLatencyEndMillis = SystemClock.elapsedRealtime();
|
||||||
|
|
||||||
|
if (mLogUtil.isPiiTraceEnabled()) {
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"search, request",
|
||||||
|
finalSearchSpec.getQuery(),
|
||||||
|
finalSearchSpec + ", " + scoringSpec + ", " + finalResultSpec);
|
||||||
|
}
|
||||||
SearchResultProto searchResultProto =
|
SearchResultProto searchResultProto =
|
||||||
mIcingSearchEngineLocked.search(
|
mIcingSearchEngineLocked.search(finalSearchSpec, scoringSpec, finalResultSpec);
|
||||||
searchSpecBuilder.build(), scoringSpec, resultSpecBuilder.build());
|
mLogUtil.piiTrace(
|
||||||
|
"search, response", searchResultProto.getResultsCount(), searchResultProto);
|
||||||
|
|
||||||
if (sStatsBuilder != null) {
|
if (sStatsBuilder != null) {
|
||||||
sStatsBuilder
|
sStatsBuilder
|
||||||
.setStatusCode(
|
.setStatusCode(statusProtoToResultCode(searchResultProto.getStatus()))
|
||||||
statusProtoToAppSearchException(searchResultProto.getStatus())
|
|
||||||
.getResultCode())
|
|
||||||
.setRewriteSearchSpecLatencyMillis(
|
.setRewriteSearchSpecLatencyMillis(
|
||||||
(int)
|
(int)
|
||||||
(rewriteSearchSpecLatencyEndMillis
|
(rewriteSearchSpecLatencyEndMillis
|
||||||
@@ -985,8 +1040,13 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
try {
|
try {
|
||||||
throwIfClosedLocked();
|
throwIfClosedLocked();
|
||||||
|
|
||||||
|
mLogUtil.piiTrace("getNextPage, request", nextPageToken);
|
||||||
SearchResultProto searchResultProto =
|
SearchResultProto searchResultProto =
|
||||||
mIcingSearchEngineLocked.getNextPage(nextPageToken);
|
mIcingSearchEngineLocked.getNextPage(nextPageToken);
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"getNextPage, response",
|
||||||
|
searchResultProto.getResultsCount(),
|
||||||
|
searchResultProto);
|
||||||
checkSuccess(searchResultProto.getStatus());
|
checkSuccess(searchResultProto.getStatus());
|
||||||
return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
|
return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -1007,6 +1067,7 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
try {
|
try {
|
||||||
throwIfClosedLocked();
|
throwIfClosedLocked();
|
||||||
|
|
||||||
|
mLogUtil.piiTrace("invalidateNextPageToken, request", nextPageToken);
|
||||||
mIcingSearchEngineLocked.invalidateNextPageToken(nextPageToken);
|
mIcingSearchEngineLocked.invalidateNextPageToken(nextPageToken);
|
||||||
} finally {
|
} finally {
|
||||||
mReadWriteLock.readLock().unlock();
|
mReadWriteLock.readLock().unlock();
|
||||||
@@ -1039,7 +1100,9 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
.setUsageType(usageType)
|
.setUsageType(usageType)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
mLogUtil.piiTrace("reportUsage, request", report.getDocumentUri(), report);
|
||||||
ReportUsageResultProto result = mIcingSearchEngineLocked.reportUsage(report);
|
ReportUsageResultProto result = mIcingSearchEngineLocked.reportUsage(report);
|
||||||
|
mLogUtil.piiTrace("reportUsage, response", result.getStatus(), result);
|
||||||
checkSuccess(result.getStatus());
|
checkSuccess(result.getStatus());
|
||||||
} finally {
|
} finally {
|
||||||
mReadWriteLock.writeLock().unlock();
|
mReadWriteLock.writeLock().unlock();
|
||||||
@@ -1068,9 +1131,13 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
throwIfClosedLocked();
|
throwIfClosedLocked();
|
||||||
|
|
||||||
String prefixedNamespace = createPrefix(packageName, databaseName) + namespace;
|
String prefixedNamespace = createPrefix(packageName, databaseName) + namespace;
|
||||||
|
if (mLogUtil.isPiiTraceEnabled()) {
|
||||||
|
mLogUtil.piiTrace("removeById, request", prefixedNamespace + ", " + id);
|
||||||
|
}
|
||||||
DeleteResultProto deleteResultProto =
|
DeleteResultProto deleteResultProto =
|
||||||
mIcingSearchEngineLocked.delete(prefixedNamespace, id);
|
mIcingSearchEngineLocked.delete(prefixedNamespace, id);
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"removeById, response", deleteResultProto.getStatus(), deleteResultProto);
|
||||||
checkSuccess(deleteResultProto.getStatus());
|
checkSuccess(deleteResultProto.getStatus());
|
||||||
} finally {
|
} finally {
|
||||||
mReadWriteLock.writeLock().unlock();
|
mReadWriteLock.writeLock().unlock();
|
||||||
@@ -1121,8 +1188,12 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
searchSpecBuilder, Collections.singleton(prefix), allowedPrefixedSchemas)) {
|
searchSpecBuilder, Collections.singleton(prefix), allowedPrefixedSchemas)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
SearchSpecProto finalSearchSpec = searchSpecBuilder.build();
|
||||||
|
mLogUtil.piiTrace("removeByQuery, request", finalSearchSpec);
|
||||||
DeleteByQueryResultProto deleteResultProto =
|
DeleteByQueryResultProto deleteResultProto =
|
||||||
mIcingSearchEngineLocked.deleteByQuery(searchSpecBuilder.build());
|
mIcingSearchEngineLocked.deleteByQuery(finalSearchSpec);
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"removeByQuery, response", deleteResultProto.getStatus(), deleteResultProto);
|
||||||
|
|
||||||
// It seems that the caller wants to get success if the data matching the query is
|
// 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.
|
// not in the DB because it was not there or was successfully deleted.
|
||||||
@@ -1202,7 +1273,10 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
@NonNull
|
@NonNull
|
||||||
private StorageInfo getStorageInfoForNamespacesLocked(@NonNull Set<String> prefixedNamespaces)
|
private StorageInfo getStorageInfoForNamespacesLocked(@NonNull Set<String> prefixedNamespaces)
|
||||||
throws AppSearchException {
|
throws AppSearchException {
|
||||||
|
mLogUtil.piiTrace("getStorageInfo, request");
|
||||||
StorageInfoResultProto storageInfoResult = mIcingSearchEngineLocked.getStorageInfo();
|
StorageInfoResultProto storageInfoResult = mIcingSearchEngineLocked.getStorageInfo();
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"getStorageInfo, response", storageInfoResult.getStatus(), storageInfoResult);
|
||||||
checkSuccess(storageInfoResult.getStatus());
|
checkSuccess(storageInfoResult.getStatus());
|
||||||
if (!storageInfoResult.hasStorageInfo()
|
if (!storageInfoResult.hasStorageInfo()
|
||||||
|| !storageInfoResult.getStorageInfo().hasDocumentStorageInfo()) {
|
|| !storageInfoResult.getStorageInfo().hasDocumentStorageInfo()) {
|
||||||
@@ -1277,8 +1351,13 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
try {
|
try {
|
||||||
throwIfClosedLocked();
|
throwIfClosedLocked();
|
||||||
|
|
||||||
|
mLogUtil.piiTrace("persistToDisk, request", persistType);
|
||||||
PersistToDiskResultProto persistToDiskResultProto =
|
PersistToDiskResultProto persistToDiskResultProto =
|
||||||
mIcingSearchEngineLocked.persistToDisk(persistType);
|
mIcingSearchEngineLocked.persistToDisk(persistType);
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"persistToDisk, response",
|
||||||
|
persistToDiskResultProto.getStatus(),
|
||||||
|
persistToDiskResultProto);
|
||||||
checkSuccess(persistToDiskResultProto.getStatus());
|
checkSuccess(persistToDiskResultProto.getStatus());
|
||||||
} finally {
|
} finally {
|
||||||
mReadWriteLock.writeLock().unlock();
|
mReadWriteLock.writeLock().unlock();
|
||||||
@@ -1305,12 +1384,21 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
newSchemaBuilder.addTypes(existingSchema.getTypes(i));
|
newSchemaBuilder.addTypes(existingSchema.getTypes(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SchemaProto finalSchema = newSchemaBuilder.build();
|
||||||
|
|
||||||
// Apply schema, set force override to true to remove all schemas and documents under
|
// Apply schema, set force override to true to remove all schemas and documents under
|
||||||
// that package.
|
// that package.
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"clearPackageData.setSchema, request",
|
||||||
|
finalSchema.getTypesCount(),
|
||||||
|
finalSchema);
|
||||||
SetSchemaResultProto setSchemaResultProto =
|
SetSchemaResultProto setSchemaResultProto =
|
||||||
mIcingSearchEngineLocked.setSchema(
|
mIcingSearchEngineLocked.setSchema(
|
||||||
newSchemaBuilder.build(), /*ignoreErrorsAndDeleteDocuments=*/ true);
|
finalSchema, /*ignoreErrorsAndDeleteDocuments=*/ true);
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"clearPackageData.setSchema, response",
|
||||||
|
setSchemaResultProto.getStatus(),
|
||||||
|
setSchemaResultProto);
|
||||||
|
|
||||||
// Determine whether it succeeded.
|
// Determine whether it succeeded.
|
||||||
checkSuccess(setSchemaResultProto.getStatus());
|
checkSuccess(setSchemaResultProto.getStatus());
|
||||||
@@ -1330,12 +1418,24 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
* @throws AppSearchException on IcingSearchEngine error.
|
* @throws AppSearchException on IcingSearchEngine error.
|
||||||
*/
|
*/
|
||||||
@GuardedBy("mReadWriteLock")
|
@GuardedBy("mReadWriteLock")
|
||||||
private void resetLocked() throws AppSearchException {
|
private void resetLocked(@Nullable InitializeStats.Builder initStatsBuilder)
|
||||||
|
throws AppSearchException {
|
||||||
|
mLogUtil.piiTrace("icingSearchEngine.reset, request");
|
||||||
ResetResultProto resetResultProto = mIcingSearchEngineLocked.reset();
|
ResetResultProto resetResultProto = mIcingSearchEngineLocked.reset();
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"icingSearchEngine.reset, response",
|
||||||
|
resetResultProto.getStatus(),
|
||||||
|
resetResultProto);
|
||||||
mOptimizeIntervalCountLocked = 0;
|
mOptimizeIntervalCountLocked = 0;
|
||||||
mSchemaMapLocked.clear();
|
mSchemaMapLocked.clear();
|
||||||
mNamespaceMapLocked.clear();
|
mNamespaceMapLocked.clear();
|
||||||
|
|
||||||
|
if (initStatsBuilder != null) {
|
||||||
|
initStatsBuilder
|
||||||
|
.setHasReset(true)
|
||||||
|
.setResetStatusCode(statusProtoToResultCode(resetResultProto.getStatus()));
|
||||||
|
}
|
||||||
|
|
||||||
// Must be called after everything else since VisibilityStore may repopulate
|
// Must be called after everything else since VisibilityStore may repopulate
|
||||||
// IcingSearchEngine with an initial schema.
|
// IcingSearchEngine with an initial schema.
|
||||||
mVisibilityStoreLocked.handleReset();
|
mVisibilityStoreLocked.handleReset();
|
||||||
@@ -1472,6 +1572,7 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
|
|
||||||
// Empty namespaces on the search spec means to query over all namespaces.
|
// Empty namespaces on the search spec means to query over all namespaces.
|
||||||
Set<String> existingNamespaces = mNamespaceMapLocked.get(prefix);
|
Set<String> existingNamespaces = mNamespaceMapLocked.get(prefix);
|
||||||
|
if (existingNamespaces != null) {
|
||||||
if (namespaceFilters.isEmpty()) {
|
if (namespaceFilters.isEmpty()) {
|
||||||
// Include all namespaces
|
// Include all namespaces
|
||||||
searchSpecBuilder.addAllNamespaceFilters(existingNamespaces);
|
searchSpecBuilder.addAllNamespaceFilters(existingNamespaces);
|
||||||
@@ -1485,6 +1586,7 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1581,6 +1683,9 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
Map<String, List<String>> packageAndNamespaceToNamespaces = new ArrayMap<>();
|
Map<String, List<String>> packageAndNamespaceToNamespaces = new ArrayMap<>();
|
||||||
for (String prefix : existingPrefixes) {
|
for (String prefix : existingPrefixes) {
|
||||||
Set<String> prefixedNamespaces = mNamespaceMapLocked.get(prefix);
|
Set<String> prefixedNamespaces = mNamespaceMapLocked.get(prefix);
|
||||||
|
if (prefixedNamespaces == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
String packageName = getPackageName(prefix);
|
String packageName = getPackageName(prefix);
|
||||||
// Create a new prefix without the database name. This will allow us to group namespaces
|
// Create a new prefix without the database name. This will allow us to group namespaces
|
||||||
// that have the same name and package but a different database name together.
|
// that have the same name and package but a different database name together.
|
||||||
@@ -1636,6 +1741,9 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
Map<String, List<String>> packageToNamespacesMap = new ArrayMap<>();
|
Map<String, List<String>> packageToNamespacesMap = new ArrayMap<>();
|
||||||
for (String prefix : existingPrefixes) {
|
for (String prefix : existingPrefixes) {
|
||||||
Set<String> prefixedNamespaces = mNamespaceMapLocked.get(prefix);
|
Set<String> prefixedNamespaces = mNamespaceMapLocked.get(prefix);
|
||||||
|
if (prefixedNamespaces == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
String packageName = getPackageName(prefix);
|
String packageName = getPackageName(prefix);
|
||||||
List<String> packageNamespaceList = packageToNamespacesMap.get(packageName);
|
List<String> packageNamespaceList = packageToNamespacesMap.get(packageName);
|
||||||
if (packageNamespaceList == null) {
|
if (packageNamespaceList == null) {
|
||||||
@@ -1677,6 +1785,9 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
Map<String, List<String>> namespaceToPrefixedNamespaces = new ArrayMap<>();
|
Map<String, List<String>> namespaceToPrefixedNamespaces = new ArrayMap<>();
|
||||||
for (String prefix : existingPrefixes) {
|
for (String prefix : existingPrefixes) {
|
||||||
Set<String> prefixedNamespaces = mNamespaceMapLocked.get(prefix);
|
Set<String> prefixedNamespaces = mNamespaceMapLocked.get(prefix);
|
||||||
|
if (prefixedNamespaces == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (String prefixedNamespace : prefixedNamespaces) {
|
for (String prefixedNamespace : prefixedNamespaces) {
|
||||||
String namespace;
|
String namespace;
|
||||||
try {
|
try {
|
||||||
@@ -1707,7 +1818,9 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@GuardedBy("mReadWriteLock")
|
@GuardedBy("mReadWriteLock")
|
||||||
SchemaProto getSchemaProtoLocked() throws AppSearchException {
|
SchemaProto getSchemaProtoLocked() throws AppSearchException {
|
||||||
|
mLogUtil.piiTrace("getSchema, request");
|
||||||
GetSchemaResultProto schemaProto = mIcingSearchEngineLocked.getSchema();
|
GetSchemaResultProto schemaProto = mIcingSearchEngineLocked.getSchema();
|
||||||
|
mLogUtil.piiTrace("getSchema, response", schemaProto.getStatus(), schemaProto);
|
||||||
// TODO(b/161935693) check GetSchemaResultProto is success or not. Call reset() if it's not.
|
// TODO(b/161935693) check GetSchemaResultProto is success or not. Call reset() if it's not.
|
||||||
// TODO(b/161935693) only allow GetSchemaResultProto NOT_FOUND on first run
|
// TODO(b/161935693) only allow GetSchemaResultProto NOT_FOUND on first run
|
||||||
checkCodeOneOf(schemaProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
|
checkCodeOneOf(schemaProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
|
||||||
@@ -1781,7 +1894,9 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw statusProtoToAppSearchException(statusProto);
|
throw new AppSearchException(
|
||||||
|
ResultCodeToProtoConverter.toResultCode(statusProto.getCode()),
|
||||||
|
statusProto.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1849,7 +1964,10 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
public void optimize() throws AppSearchException {
|
public void optimize() throws AppSearchException {
|
||||||
mReadWriteLock.writeLock().lock();
|
mReadWriteLock.writeLock().lock();
|
||||||
try {
|
try {
|
||||||
|
mLogUtil.piiTrace("optimize, request");
|
||||||
OptimizeResultProto optimizeResultProto = mIcingSearchEngineLocked.optimize();
|
OptimizeResultProto optimizeResultProto = mIcingSearchEngineLocked.optimize();
|
||||||
|
mLogUtil.piiTrace(
|
||||||
|
"optimize, response", optimizeResultProto.getStatus(), optimizeResultProto);
|
||||||
checkSuccess(optimizeResultProto.getStatus());
|
checkSuccess(optimizeResultProto.getStatus());
|
||||||
} finally {
|
} finally {
|
||||||
mReadWriteLock.writeLock().unlock();
|
mReadWriteLock.writeLock().unlock();
|
||||||
@@ -1887,7 +2005,10 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
@GuardedBy("mReadWriteLock")
|
@GuardedBy("mReadWriteLock")
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
GetOptimizeInfoResultProto getOptimizeInfoResultLocked() {
|
GetOptimizeInfoResultProto getOptimizeInfoResultLocked() {
|
||||||
return mIcingSearchEngineLocked.getOptimizeInfo();
|
mLogUtil.piiTrace("getOptimizeInfo, request");
|
||||||
|
GetOptimizeInfoResultProto result = mIcingSearchEngineLocked.getOptimizeInfo();
|
||||||
|
mLogUtil.piiTrace("getOptimizeInfo, response", result.getStatus(), result);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("mReadWriteLock")
|
@GuardedBy("mReadWriteLock")
|
||||||
@@ -1898,16 +2019,16 @@ public final class AppSearchImpl implements Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts an erroneous status code to an AppSearchException. Callers should ensure that the
|
* Converts an erroneous status code from the Icing status enums to the AppSearchResult enums.
|
||||||
* status code is not OK or WARNING_DATA_LOSS.
|
|
||||||
*
|
*
|
||||||
* @param statusProto StatusProto with error code and message to translate into
|
* <p>Callers should ensure that the status code is not OK or WARNING_DATA_LOSS.
|
||||||
* AppSearchException.
|
*
|
||||||
* @return AppSearchException with the parallel error code.
|
* @param statusProto StatusProto with error code to translate into an {@link AppSearchResult}
|
||||||
|
* code.
|
||||||
|
* @return {@link AppSearchResult} error code
|
||||||
*/
|
*/
|
||||||
private static AppSearchException statusProtoToAppSearchException(StatusProto statusProto) {
|
private static @AppSearchResult.ResultCode int statusProtoToResultCode(
|
||||||
return new AppSearchException(
|
@NonNull StatusProto statusProto) {
|
||||||
ResultCodeToProtoConverter.toResultCode(statusProto.getCode()),
|
return ResultCodeToProtoConverter.toResultCode(statusProto.getCode());
|
||||||
statusProto.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,10 @@ public final class InitializeStats {
|
|||||||
private final int mNativeNumDocuments;
|
private final int mNativeNumDocuments;
|
||||||
/** Returns number of schema types currently in the schema store. */
|
/** Returns number of schema types currently in the schema store. */
|
||||||
private final int mNativeNumSchemaTypes;
|
private final int mNativeNumSchemaTypes;
|
||||||
|
/** Whether we had to reset the index, losing all data, during initialization. */
|
||||||
|
private final boolean mHasReset;
|
||||||
|
/** If we had to reset, contains the status code of the reset operation. */
|
||||||
|
@AppSearchResult.ResultCode private final int mResetStatusCode;
|
||||||
|
|
||||||
/** Returns the status of the initialization. */
|
/** Returns the status of the initialization. */
|
||||||
@AppSearchResult.ResultCode
|
@AppSearchResult.ResultCode
|
||||||
@@ -214,6 +218,21 @@ public final class InitializeStats {
|
|||||||
return mNativeNumSchemaTypes;
|
return mNativeNumSchemaTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns whether we had to reset the index, losing all data, as part of initialization. */
|
||||||
|
public boolean hasReset() {
|
||||||
|
return mHasReset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the status of the reset, if one was performed according to {@link #hasReset}.
|
||||||
|
*
|
||||||
|
* <p>If no value has been set, the default value is {@link AppSearchResult#RESULT_OK}.
|
||||||
|
*/
|
||||||
|
@AppSearchResult.ResultCode
|
||||||
|
public int getResetStatusCode() {
|
||||||
|
return mResetStatusCode;
|
||||||
|
}
|
||||||
|
|
||||||
InitializeStats(@NonNull Builder builder) {
|
InitializeStats(@NonNull Builder builder) {
|
||||||
Objects.requireNonNull(builder);
|
Objects.requireNonNull(builder);
|
||||||
mStatusCode = builder.mStatusCode;
|
mStatusCode = builder.mStatusCode;
|
||||||
@@ -232,11 +251,14 @@ public final class InitializeStats {
|
|||||||
mNativeDocumentStoreDataStatus = builder.mNativeDocumentStoreDataStatus;
|
mNativeDocumentStoreDataStatus = builder.mNativeDocumentStoreDataStatus;
|
||||||
mNativeNumDocuments = builder.mNativeNumDocuments;
|
mNativeNumDocuments = builder.mNativeNumDocuments;
|
||||||
mNativeNumSchemaTypes = builder.mNativeNumSchemaTypes;
|
mNativeNumSchemaTypes = builder.mNativeNumSchemaTypes;
|
||||||
|
mHasReset = builder.mHasReset;
|
||||||
|
mResetStatusCode = builder.mResetStatusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Builder for {@link InitializeStats}. */
|
/** Builder for {@link InitializeStats}. */
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
@AppSearchResult.ResultCode int mStatusCode;
|
@AppSearchResult.ResultCode int mStatusCode;
|
||||||
|
|
||||||
int mTotalLatencyMillis;
|
int mTotalLatencyMillis;
|
||||||
boolean mHasDeSync;
|
boolean mHasDeSync;
|
||||||
int mPrepareSchemaAndNamespacesLatencyMillis;
|
int mPrepareSchemaAndNamespacesLatencyMillis;
|
||||||
@@ -251,6 +273,8 @@ public final class InitializeStats {
|
|||||||
@DocumentStoreDataStatus int mNativeDocumentStoreDataStatus;
|
@DocumentStoreDataStatus int mNativeDocumentStoreDataStatus;
|
||||||
int mNativeNumDocuments;
|
int mNativeNumDocuments;
|
||||||
int mNativeNumSchemaTypes;
|
int mNativeNumSchemaTypes;
|
||||||
|
boolean mHasReset;
|
||||||
|
@AppSearchResult.ResultCode int mResetStatusCode;
|
||||||
|
|
||||||
/** Sets the status of the initialization. */
|
/** Sets the status of the initialization. */
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -392,6 +416,20 @@ public final class InitializeStats {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sets whether we had to reset the index, losing all data, as part of initialization. */
|
||||||
|
@NonNull
|
||||||
|
public Builder setHasReset(boolean hasReset) {
|
||||||
|
mHasReset = hasReset;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets the status of the reset, if one was performed according to {@link #setHasReset}. */
|
||||||
|
@NonNull
|
||||||
|
public Builder setResetStatusCode(@AppSearchResult.ResultCode int resetStatusCode) {
|
||||||
|
mResetStatusCode = resetStatusCode;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@link InitializeStats} from the contents of this {@link
|
* Constructs a new {@link InitializeStats} from the contents of this {@link
|
||||||
* InitializeStats.Builder}
|
* InitializeStats.Builder}
|
||||||
|
|||||||
@@ -112,8 +112,11 @@ public class PrefixUtil {
|
|||||||
return prefixedString.substring(delimiterIndex + 1);
|
return prefixedString.substring(delimiterIndex + 1);
|
||||||
}
|
}
|
||||||
throw new AppSearchException(
|
throw new AppSearchException(
|
||||||
AppSearchResult.RESULT_UNKNOWN_ERROR,
|
AppSearchResult.RESULT_INTERNAL_ERROR,
|
||||||
"The prefixed value doesn't contains a valid database name.");
|
"The prefixed value \""
|
||||||
|
+ prefixedString
|
||||||
|
+ "\" doesn't contain a valid "
|
||||||
|
+ "database name");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,8 +131,11 @@ public class PrefixUtil {
|
|||||||
int databaseDelimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER);
|
int databaseDelimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER);
|
||||||
if (databaseDelimiterIndex == -1) {
|
if (databaseDelimiterIndex == -1) {
|
||||||
throw new AppSearchException(
|
throw new AppSearchException(
|
||||||
AppSearchResult.RESULT_UNKNOWN_ERROR,
|
AppSearchResult.RESULT_INTERNAL_ERROR,
|
||||||
"The databaseName prefixed value doesn't contain a valid database name.");
|
"The prefixed value \""
|
||||||
|
+ prefixedString
|
||||||
|
+ "\" doesn't contain a valid "
|
||||||
|
+ "database name");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add 1 to include the char size of the DATABASE_DELIMITER
|
// Add 1 to include the char size of the DATABASE_DELIMITER
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
I0216abecc41d020f16ed8947a9f37b710afd331e
|
a83c33a5a394141fea1d065ce0fab513a62d4bcf
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ import java.util.Set;
|
|||||||
* <p>An {@link AppSearchSessionShim} instance provides access to database operations such as
|
* <p>An {@link AppSearchSessionShim} instance provides access to database operations such as
|
||||||
* setting a schema, adding documents, and searching.
|
* setting a schema, adding documents, and searching.
|
||||||
*
|
*
|
||||||
|
* <p>Instances of this interface are usually obtained from a storage implementation, e.g. {@code
|
||||||
|
* AppSearchManager.createSearchSession()} or {@code PlatformStorage.createSearchSession()}.
|
||||||
|
*
|
||||||
* <p>All implementations of this interface must be thread safe.
|
* <p>All implementations of this interface must be thread safe.
|
||||||
*
|
*
|
||||||
* @see GlobalSearchSessionShim
|
* @see GlobalSearchSessionShim
|
||||||
|
|||||||
@@ -1,186 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package android.app.appsearch;
|
|
||||||
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.testng.Assert.expectThrows;
|
|
||||||
|
|
||||||
import android.util.ArrayMap;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class SetSchemaRequestTest {
|
|
||||||
|
|
||||||
private static Collection<String> getSchemaTypesFromSetSchemaRequest(SetSchemaRequest request) {
|
|
||||||
HashSet<String> schemaTypes = new HashSet<>();
|
|
||||||
for (AppSearchSchema schema : request.getSchemas()) {
|
|
||||||
schemaTypes.add(schema.getSchemaType());
|
|
||||||
}
|
|
||||||
return schemaTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInvalidSchemaReferences_fromDisplayedBySystem() {
|
|
||||||
IllegalArgumentException expected =
|
|
||||||
expectThrows(
|
|
||||||
IllegalArgumentException.class,
|
|
||||||
() ->
|
|
||||||
new SetSchemaRequest.Builder()
|
|
||||||
.setSchemaTypeDisplayedBySystem("InvalidSchema", false)
|
|
||||||
.build());
|
|
||||||
assertThat(expected).hasMessageThat().contains("referenced, but were not added");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInvalidSchemaReferences_fromPackageVisibility() {
|
|
||||||
IllegalArgumentException expected =
|
|
||||||
expectThrows(
|
|
||||||
IllegalArgumentException.class,
|
|
||||||
() ->
|
|
||||||
new SetSchemaRequest.Builder()
|
|
||||||
.setSchemaTypeVisibilityForPackage(
|
|
||||||
"InvalidSchema",
|
|
||||||
/*visible=*/ true,
|
|
||||||
new PackageIdentifier(
|
|
||||||
"com.foo.package",
|
|
||||||
/*sha256Certificate=*/ new byte[] {}))
|
|
||||||
.build());
|
|
||||||
assertThat(expected).hasMessageThat().contains("referenced, but were not added");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetSchemaTypeDisplayedBySystem_displayed() {
|
|
||||||
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
|
|
||||||
|
|
||||||
// By default, the schema is displayed.
|
|
||||||
SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
|
|
||||||
assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty();
|
|
||||||
|
|
||||||
request =
|
|
||||||
new SetSchemaRequest.Builder()
|
|
||||||
.addSchemas(schema)
|
|
||||||
.setSchemaTypeDisplayedBySystem("Schema", true)
|
|
||||||
.build();
|
|
||||||
assertThat(request.getSchemasNotDisplayedBySystem()).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetSchemaTypeDisplayedBySystem_notDisplayed() {
|
|
||||||
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
|
|
||||||
SetSchemaRequest request =
|
|
||||||
new SetSchemaRequest.Builder()
|
|
||||||
.addSchemas(schema)
|
|
||||||
.setSchemaTypeDisplayedBySystem("Schema", false)
|
|
||||||
.build();
|
|
||||||
assertThat(request.getSchemasNotDisplayedBySystem()).containsExactly("Schema");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSchemaTypeVisibilityForPackage_visible() {
|
|
||||||
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
|
|
||||||
|
|
||||||
// By default, the schema is not visible.
|
|
||||||
SetSchemaRequest request = new SetSchemaRequest.Builder().addSchemas(schema).build();
|
|
||||||
assertThat(request.getSchemasVisibleToPackages()).isEmpty();
|
|
||||||
|
|
||||||
PackageIdentifier packageIdentifier =
|
|
||||||
new PackageIdentifier("com.package.foo", new byte[] {100});
|
|
||||||
Map<String, Set<PackageIdentifier>> expectedVisibleToPackagesMap = new ArrayMap<>();
|
|
||||||
expectedVisibleToPackagesMap.put("Schema", Collections.singleton(packageIdentifier));
|
|
||||||
|
|
||||||
request =
|
|
||||||
new SetSchemaRequest.Builder()
|
|
||||||
.addSchemas(schema)
|
|
||||||
.setSchemaTypeVisibilityForPackage(
|
|
||||||
"Schema", /*visible=*/ true, packageIdentifier)
|
|
||||||
.build();
|
|
||||||
assertThat(request.getSchemasVisibleToPackages())
|
|
||||||
.containsExactlyEntriesIn(expectedVisibleToPackagesMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSchemaTypeVisibilityForPackage_notVisible() {
|
|
||||||
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
|
|
||||||
|
|
||||||
SetSchemaRequest request =
|
|
||||||
new SetSchemaRequest.Builder()
|
|
||||||
.addSchemas(schema)
|
|
||||||
.setSchemaTypeVisibilityForPackage(
|
|
||||||
"Schema",
|
|
||||||
/*visible=*/ false,
|
|
||||||
new PackageIdentifier(
|
|
||||||
"com.package.foo", /*sha256Certificate=*/ new byte[] {}))
|
|
||||||
.build();
|
|
||||||
assertThat(request.getSchemasVisibleToPackages()).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSchemaTypeVisibilityForPackage_deduped() throws Exception {
|
|
||||||
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
|
|
||||||
|
|
||||||
PackageIdentifier packageIdentifier =
|
|
||||||
new PackageIdentifier("com.package.foo", new byte[] {100});
|
|
||||||
Map<String, Set<PackageIdentifier>> expectedVisibleToPackagesMap = new ArrayMap<>();
|
|
||||||
expectedVisibleToPackagesMap.put("Schema", Collections.singleton(packageIdentifier));
|
|
||||||
|
|
||||||
SetSchemaRequest request =
|
|
||||||
new SetSchemaRequest.Builder()
|
|
||||||
.addSchemas(schema)
|
|
||||||
// Set it visible for "Schema"
|
|
||||||
.setSchemaTypeVisibilityForPackage(
|
|
||||||
"Schema", /*visible=*/ true, packageIdentifier)
|
|
||||||
// Set it visible for "Schema" again, which should be a no-op
|
|
||||||
.setSchemaTypeVisibilityForPackage(
|
|
||||||
"Schema", /*visible=*/ true, packageIdentifier)
|
|
||||||
.build();
|
|
||||||
assertThat(request.getSchemasVisibleToPackages())
|
|
||||||
.containsExactlyEntriesIn(expectedVisibleToPackagesMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSchemaTypeVisibilityForPackage_removed() throws Exception {
|
|
||||||
AppSearchSchema schema = new AppSearchSchema.Builder("Schema").build();
|
|
||||||
|
|
||||||
SetSchemaRequest request =
|
|
||||||
new SetSchemaRequest.Builder()
|
|
||||||
.addSchemas(schema)
|
|
||||||
// First set it as visible
|
|
||||||
.setSchemaTypeVisibilityForPackage(
|
|
||||||
"Schema",
|
|
||||||
/*visible=*/ true,
|
|
||||||
new PackageIdentifier(
|
|
||||||
"com.package.foo", /*sha256Certificate=*/ new byte[] {100}))
|
|
||||||
// Then make it not visible
|
|
||||||
.setSchemaTypeVisibilityForPackage(
|
|
||||||
"Schema",
|
|
||||||
/*visible=*/ false,
|
|
||||||
new PackageIdentifier(
|
|
||||||
"com.package.foo", /*sha256Certificate=*/ new byte[] {100}))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// Nothing should be visible.
|
|
||||||
assertThat(request.getSchemasVisibleToPackages()).isEmpty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
|
|
||||||
import static org.testng.Assert.expectThrows;
|
import static org.testng.Assert.expectThrows;
|
||||||
|
|
||||||
|
import android.app.appsearch.AppSearchResult;
|
||||||
import android.app.appsearch.AppSearchSchema;
|
import android.app.appsearch.AppSearchSchema;
|
||||||
import android.app.appsearch.GenericDocument;
|
import android.app.appsearch.GenericDocument;
|
||||||
import android.app.appsearch.SearchResult;
|
import android.app.appsearch.SearchResult;
|
||||||
@@ -39,16 +40,19 @@ import android.util.ArraySet;
|
|||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
|
import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
|
||||||
|
import com.android.server.appsearch.external.localstorage.stats.InitializeStats;
|
||||||
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
|
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
|
||||||
import com.android.server.appsearch.proto.DocumentProto;
|
import com.android.server.appsearch.proto.DocumentProto;
|
||||||
import com.android.server.appsearch.proto.GetOptimizeInfoResultProto;
|
import com.android.server.appsearch.proto.GetOptimizeInfoResultProto;
|
||||||
import com.android.server.appsearch.proto.PersistType;
|
import com.android.server.appsearch.proto.PersistType;
|
||||||
import com.android.server.appsearch.proto.PropertyConfigProto;
|
import com.android.server.appsearch.proto.PropertyConfigProto;
|
||||||
import com.android.server.appsearch.proto.PropertyProto;
|
import com.android.server.appsearch.proto.PropertyProto;
|
||||||
|
import com.android.server.appsearch.proto.PutResultProto;
|
||||||
import com.android.server.appsearch.proto.SchemaProto;
|
import com.android.server.appsearch.proto.SchemaProto;
|
||||||
import com.android.server.appsearch.proto.SchemaTypeConfigProto;
|
import com.android.server.appsearch.proto.SchemaTypeConfigProto;
|
||||||
import com.android.server.appsearch.proto.SearchResultProto;
|
import com.android.server.appsearch.proto.SearchResultProto;
|
||||||
import com.android.server.appsearch.proto.SearchSpecProto;
|
import com.android.server.appsearch.proto.SearchSpecProto;
|
||||||
|
import com.android.server.appsearch.proto.StatusProto;
|
||||||
import com.android.server.appsearch.proto.StringIndexingConfig;
|
import com.android.server.appsearch.proto.StringIndexingConfig;
|
||||||
import com.android.server.appsearch.proto.TermMatchType;
|
import com.android.server.appsearch.proto.TermMatchType;
|
||||||
|
|
||||||
@@ -86,8 +90,6 @@ public class AppSearchImplTest {
|
|||||||
/*logger=*/ null);
|
/*logger=*/ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(b/175430168) add test to verify reset is working properly.
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure that we can rewrite an incoming schema type by adding the database as a prefix. While
|
* Ensure that we can rewrite an incoming schema type by adding the database as a prefix. While
|
||||||
* also keeping any other existing schema types that may already be part of Icing's persisted
|
* also keeping any other existing schema types that may already be part of Icing's persisted
|
||||||
@@ -485,6 +487,142 @@ public class AppSearchImplTest {
|
|||||||
.isLessThan(AppSearchImpl.CHECK_OPTIMIZE_INTERVAL);
|
.isLessThan(AppSearchImpl.CHECK_OPTIMIZE_INTERVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReset() throws Exception {
|
||||||
|
// Setup the index
|
||||||
|
Context context = ApplicationProvider.getApplicationContext();
|
||||||
|
File appsearchDir = mTemporaryFolder.newFolder();
|
||||||
|
AppSearchImpl appSearchImpl =
|
||||||
|
AppSearchImpl.create(
|
||||||
|
appsearchDir,
|
||||||
|
context,
|
||||||
|
VisibilityStore.NO_OP_USER_ID,
|
||||||
|
/*globalQuerierPackage=*/ "",
|
||||||
|
/*logger=*/ null);
|
||||||
|
|
||||||
|
// Insert schema
|
||||||
|
List<AppSearchSchema> schemas =
|
||||||
|
ImmutableList.of(
|
||||||
|
new AppSearchSchema.Builder("Type1").build(),
|
||||||
|
new AppSearchSchema.Builder("Type2").build());
|
||||||
|
appSearchImpl.setSchema(
|
||||||
|
context.getPackageName(),
|
||||||
|
"database1",
|
||||||
|
schemas,
|
||||||
|
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||||
|
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||||
|
/*forceOverride=*/ false,
|
||||||
|
/*version=*/ 0);
|
||||||
|
|
||||||
|
// Insert a valid doc
|
||||||
|
GenericDocument validDoc =
|
||||||
|
new GenericDocument.Builder<>("namespace1", "id1", "Type1").build();
|
||||||
|
appSearchImpl.putDocument(
|
||||||
|
context.getPackageName(), "database1", validDoc, /*logger=*/ null);
|
||||||
|
|
||||||
|
// Query it via global query. We use the same code again later so this is to make sure we
|
||||||
|
// have our global query configured right.
|
||||||
|
SearchResultPage results =
|
||||||
|
appSearchImpl.globalQuery(
|
||||||
|
/*queryExpression=*/ "",
|
||||||
|
new SearchSpec.Builder().addFilterSchemas("Type1").build(),
|
||||||
|
context.getPackageName(),
|
||||||
|
VisibilityStore.NO_OP_USER_ID,
|
||||||
|
/*logger=*/ null);
|
||||||
|
assertThat(results.getResults()).hasSize(1);
|
||||||
|
assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc);
|
||||||
|
|
||||||
|
// Create a doc with a malformed namespace
|
||||||
|
DocumentProto invalidDoc =
|
||||||
|
DocumentProto.newBuilder()
|
||||||
|
.setNamespace("invalidNamespace")
|
||||||
|
.setUri("id2")
|
||||||
|
.setSchema(context.getPackageName() + "$database1/Type1")
|
||||||
|
.build();
|
||||||
|
AppSearchException e =
|
||||||
|
expectThrows(
|
||||||
|
AppSearchException.class,
|
||||||
|
() -> PrefixUtil.getPrefix(invalidDoc.getNamespace()));
|
||||||
|
assertThat(e)
|
||||||
|
.hasMessageThat()
|
||||||
|
.isEqualTo(
|
||||||
|
"The prefixed value \"invalidNamespace\" doesn't contain a valid database"
|
||||||
|
+ " name");
|
||||||
|
|
||||||
|
// Insert the invalid doc with an invalid namespace right into icing
|
||||||
|
PutResultProto putResultProto = appSearchImpl.mIcingSearchEngineLocked.put(invalidDoc);
|
||||||
|
assertThat(putResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.OK);
|
||||||
|
|
||||||
|
// Create a logger for capturing initialization to make sure we are logging the recovery
|
||||||
|
// process correctly.
|
||||||
|
AppSearchLoggerTest.TestLogger testLogger = new AppSearchLoggerTest.TestLogger();
|
||||||
|
|
||||||
|
// Initialize AppSearchImpl. This should cause a reset.
|
||||||
|
appSearchImpl.close();
|
||||||
|
appSearchImpl =
|
||||||
|
AppSearchImpl.create(
|
||||||
|
appsearchDir,
|
||||||
|
context,
|
||||||
|
VisibilityStore.NO_OP_USER_ID,
|
||||||
|
/*globalQuerierPackage=*/ context.getPackageName(),
|
||||||
|
testLogger);
|
||||||
|
|
||||||
|
// Check recovery state
|
||||||
|
InitializeStats initStats = testLogger.mInitializeStats;
|
||||||
|
assertThat(initStats).isNotNull();
|
||||||
|
assertThat(initStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR);
|
||||||
|
assertThat(initStats.hasDeSync()).isFalse();
|
||||||
|
assertThat(initStats.getDocumentStoreRecoveryCause())
|
||||||
|
.isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
|
||||||
|
// TODO(b/187879464): There should not be a recovery here, but icing lib reports one if the
|
||||||
|
// doc had no tokens. Once the mentioned bug is fixed, uncomment this.
|
||||||
|
// assertThat(initStats.getIndexRestorationCause())
|
||||||
|
// .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
|
||||||
|
assertThat(initStats.getSchemaStoreRecoveryCause())
|
||||||
|
.isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE);
|
||||||
|
assertThat(initStats.getDocumentStoreDataStatus())
|
||||||
|
.isEqualTo(InitializeStats.DOCUMENT_STORE_DATA_STATUS_NO_DATA_LOSS);
|
||||||
|
assertThat(initStats.hasReset()).isTrue();
|
||||||
|
assertThat(initStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_OK);
|
||||||
|
|
||||||
|
// Make sure all our data is gone
|
||||||
|
assertThat(appSearchImpl.getSchema(context.getPackageName(), "database1").getSchemas())
|
||||||
|
.isEmpty();
|
||||||
|
results =
|
||||||
|
appSearchImpl.globalQuery(
|
||||||
|
/*queryExpression=*/ "",
|
||||||
|
new SearchSpec.Builder().addFilterSchemas("Type1").build(),
|
||||||
|
context.getPackageName(),
|
||||||
|
VisibilityStore.NO_OP_USER_ID,
|
||||||
|
/*logger=*/ null);
|
||||||
|
assertThat(results.getResults()).isEmpty();
|
||||||
|
|
||||||
|
// Make sure the index can now be used successfully
|
||||||
|
appSearchImpl.setSchema(
|
||||||
|
context.getPackageName(),
|
||||||
|
"database1",
|
||||||
|
Collections.singletonList(new AppSearchSchema.Builder("Type1").build()),
|
||||||
|
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||||
|
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||||
|
/*forceOverride=*/ false,
|
||||||
|
/*version=*/ 0);
|
||||||
|
|
||||||
|
// Insert a valid doc
|
||||||
|
appSearchImpl.putDocument(
|
||||||
|
context.getPackageName(), "database1", validDoc, /*logger=*/ null);
|
||||||
|
|
||||||
|
// Query it via global query.
|
||||||
|
results =
|
||||||
|
appSearchImpl.globalQuery(
|
||||||
|
/*queryExpression=*/ "",
|
||||||
|
new SearchSpec.Builder().addFilterSchemas("Type1").build(),
|
||||||
|
context.getPackageName(),
|
||||||
|
VisibilityStore.NO_OP_USER_ID,
|
||||||
|
/*logger=*/ null);
|
||||||
|
assertThat(results.getResults()).hasSize(1);
|
||||||
|
assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRewriteSearchSpec_oneInstance() throws Exception {
|
public void testRewriteSearchSpec_oneInstance() throws Exception {
|
||||||
SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery("");
|
SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery("");
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class AppSearchLoggerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Test only not thread safe.
|
// Test only not thread safe.
|
||||||
public class TestLogger implements AppSearchLogger {
|
public static class TestLogger implements AppSearchLogger {
|
||||||
@Nullable CallStats mCallStats;
|
@Nullable CallStats mCallStats;
|
||||||
@Nullable PutDocumentStats mPutDocumentStats;
|
@Nullable PutDocumentStats mPutDocumentStats;
|
||||||
@Nullable InitializeStats mInitializeStats;
|
@Nullable InitializeStats mInitializeStats;
|
||||||
|
|||||||
@@ -168,7 +168,9 @@ public class AppSearchStatsTest {
|
|||||||
.setSchemaStoreRecoveryLatencyMillis(nativeSchemaStoreRecoveryLatencyMillis)
|
.setSchemaStoreRecoveryLatencyMillis(nativeSchemaStoreRecoveryLatencyMillis)
|
||||||
.setDocumentStoreDataStatus(nativeDocumentStoreDataStatus)
|
.setDocumentStoreDataStatus(nativeDocumentStoreDataStatus)
|
||||||
.setDocumentCount(nativeNumDocuments)
|
.setDocumentCount(nativeNumDocuments)
|
||||||
.setSchemaTypeCount(nativeNumSchemaTypes);
|
.setSchemaTypeCount(nativeNumSchemaTypes)
|
||||||
|
.setHasReset(true)
|
||||||
|
.setResetStatusCode(AppSearchResult.RESULT_INVALID_SCHEMA);
|
||||||
final InitializeStats iStats = iStatsBuilder.build();
|
final InitializeStats iStats = iStatsBuilder.build();
|
||||||
|
|
||||||
assertThat(iStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
|
assertThat(iStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE);
|
||||||
@@ -192,6 +194,8 @@ public class AppSearchStatsTest {
|
|||||||
assertThat(iStats.getDocumentStoreDataStatus()).isEqualTo(nativeDocumentStoreDataStatus);
|
assertThat(iStats.getDocumentStoreDataStatus()).isEqualTo(nativeDocumentStoreDataStatus);
|
||||||
assertThat(iStats.getDocumentCount()).isEqualTo(nativeNumDocuments);
|
assertThat(iStats.getDocumentCount()).isEqualTo(nativeNumDocuments);
|
||||||
assertThat(iStats.getSchemaTypeCount()).isEqualTo(nativeNumSchemaTypes);
|
assertThat(iStats.getSchemaTypeCount()).isEqualTo(nativeNumSchemaTypes);
|
||||||
|
assertThat(iStats.hasReset()).isTrue();
|
||||||
|
assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_INVALID_SCHEMA);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user