summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java45
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java7
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/FrameworkLimitConfig.java41
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java201
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/LimitConfig.java57
-rw-r--r--apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/UnlimitedLimitConfig.java37
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java20
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appsearch/FrameworkLimitConfigTest.java68
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java840
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java61
-rw-r--r--services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java6
12 files changed, 1327 insertions, 61 deletions
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java
index d5271a6cb92e..689aa1fcd371 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java
@@ -60,6 +60,11 @@ public final class AppSearchConfig implements AutoCloseable {
@VisibleForTesting
static final int DEFAULT_SAMPLING_INTERVAL = 10;
+ @VisibleForTesting
+ static final int DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES = 512 * 1024; // 512KiB
+ @VisibleForTesting
+ static final int DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT = 20_000;
+
/*
* Keys for ALL the flags stored in DeviceConfig.
*/
@@ -70,13 +75,19 @@ public final class AppSearchConfig implements AutoCloseable {
"sampling_interval_for_batch_call_stats";
public static final String KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS =
"sampling_interval_for_put_document_stats";
+ public static final String KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES =
+ "limit_config_max_document_size_bytes";
+ public static final String KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT =
+ "limit_config_max_document_docunt";
// Array contains all the corresponding keys for the cached values.
private static final String[] KEYS_TO_ALL_CACHED_VALUES = {
KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS,
KEY_SAMPLING_INTERVAL_DEFAULT,
KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS,
- KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS
+ KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS,
+ KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES,
+ KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT,
};
// Lock needed for all the operations in this class.
@@ -222,6 +233,24 @@ public final class AppSearchConfig implements AutoCloseable {
}
}
+ /** Returns the maximum serialized size an indexed document can be, in bytes. */
+ public int getCachedLimitConfigMaxDocumentSizeBytes() {
+ synchronized (mLock) {
+ throwIfClosedLocked();
+ return mBundleLocked.getInt(KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES,
+ DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES);
+ }
+ }
+
+ /** Returns the maximum number of active docs allowed per package. */
+ public int getCachedLimitConfigMaxDocumentCount() {
+ synchronized (mLock) {
+ throwIfClosedLocked();
+ return mBundleLocked.getInt(KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT,
+ DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT);
+ }
+ }
+
@GuardedBy("mLock")
private void throwIfClosedLocked() {
if (mIsClosedLocked) {
@@ -264,6 +293,20 @@ public final class AppSearchConfig implements AutoCloseable {
mBundleLocked.putInt(key, properties.getInt(key, DEFAULT_SAMPLING_INTERVAL));
}
break;
+ case KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES:
+ synchronized (mLock) {
+ mBundleLocked.putInt(
+ key,
+ properties.getInt(key, DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES));
+ }
+ break;
+ case KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT:
+ synchronized (mLock) {
+ mBundleLocked.putInt(
+ key,
+ properties.getInt(key, DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT));
+ }
+ break;
default:
break;
}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
index e067d4bcdf72..d0d2e8964cf0 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
@@ -173,8 +173,11 @@ public final class AppSearchUserInstanceManager {
File appSearchDir = getAppSearchDir(userHandle);
File icingDir = new File(appSearchDir, "icing");
Log.i(TAG, "Creating new AppSearch instance at: " + icingDir);
- AppSearchImpl appSearchImpl =
- AppSearchImpl.create(icingDir, initStatsBuilder, new FrameworkOptimizeStrategy());
+ AppSearchImpl appSearchImpl = AppSearchImpl.create(
+ icingDir,
+ new FrameworkLimitConfig(config),
+ initStatsBuilder,
+ new FrameworkOptimizeStrategy());
long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime();
VisibilityStoreImpl visibilityStore =
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/FrameworkLimitConfig.java b/apex/appsearch/service/java/com/android/server/appsearch/FrameworkLimitConfig.java
new file mode 100644
index 000000000000..d16168a915d5
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/FrameworkLimitConfig.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 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;
+
+import android.annotation.NonNull;
+
+import com.android.server.appsearch.external.localstorage.LimitConfig;
+
+import java.util.Objects;
+
+class FrameworkLimitConfig implements LimitConfig {
+ private final AppSearchConfig mAppSearchConfig;
+
+ FrameworkLimitConfig(@NonNull AppSearchConfig appSearchConfig) {
+ mAppSearchConfig = Objects.requireNonNull(appSearchConfig);
+ }
+
+ @Override
+ public int getMaxDocumentSizeBytes() {
+ return mAppSearchConfig.getCachedLimitConfigMaxDocumentSizeBytes();
+ }
+
+ @Override
+ public int getMaxDocumentCount() {
+ return mAppSearchConfig.getCachedLimitConfigMaxDocumentCount();
+ }
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 9dee179bd6f2..a1b93ce12975 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -88,6 +88,7 @@ import com.google.android.icing.proto.SearchResultProto;
import com.google.android.icing.proto.SearchSpecProto;
import com.google.android.icing.proto.SetSchemaResultProto;
import com.google.android.icing.proto.StatusProto;
+import com.google.android.icing.proto.StorageInfoProto;
import com.google.android.icing.proto.StorageInfoResultProto;
import com.google.android.icing.proto.TypePropertyMask;
import com.google.android.icing.proto.UsageReport;
@@ -147,10 +148,9 @@ public final class AppSearchImpl implements Closeable {
@VisibleForTesting static final int CHECK_OPTIMIZE_INTERVAL = 100;
private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
-
private final LogUtil mLogUtil = new LogUtil(TAG);
-
private final OptimizeStrategy mOptimizeStrategy;
+ private final LimitConfig mLimitConfig;
@GuardedBy("mReadWriteLock")
@VisibleForTesting
@@ -169,6 +169,10 @@ public final class AppSearchImpl implements Closeable {
@GuardedBy("mReadWriteLock")
private final Map<String, Set<String>> mNamespaceMapLocked = new HashMap<>();
+ /** Maps package name to active document count. */
+ @GuardedBy("mReadWriteLock")
+ private final Map<String, Integer> mDocumentCountMapLocked = new ArrayMap<>();
+
/**
* The counter to check when to call {@link #checkForOptimize}. The interval is {@link
* #CHECK_OPTIMIZE_INTERVAL}.
@@ -196,19 +200,22 @@ public final class AppSearchImpl implements Closeable {
@NonNull
public static AppSearchImpl create(
@NonNull File icingDir,
+ @NonNull LimitConfig limitConfig,
@Nullable InitializeStats.Builder initStatsBuilder,
@NonNull OptimizeStrategy optimizeStrategy)
throws AppSearchException {
- return new AppSearchImpl(icingDir, initStatsBuilder, optimizeStrategy);
+ return new AppSearchImpl(icingDir, limitConfig, initStatsBuilder, optimizeStrategy);
}
/** @param initStatsBuilder collects stats for initialization if provided. */
private AppSearchImpl(
@NonNull File icingDir,
+ @NonNull LimitConfig limitConfig,
@Nullable InitializeStats.Builder initStatsBuilder,
@NonNull OptimizeStrategy optimizeStrategy)
throws AppSearchException {
Objects.requireNonNull(icingDir);
+ mLimitConfig = Objects.requireNonNull(limitConfig);
mOptimizeStrategy = Objects.requireNonNull(optimizeStrategy);
mReadWriteLock.writeLock().lock();
@@ -244,9 +251,9 @@ public final class AppSearchImpl implements Closeable {
AppSearchLoggerHelper.copyNativeStats(
initializeResultProto.getInitializeStats(), initStatsBuilder);
}
-
checkSuccess(initializeResultProto.getStatus());
+ // Read all protos we need to construct AppSearchImpl's cache maps
long prepareSchemaAndNamespacesLatencyStartMillis = SystemClock.elapsedRealtime();
SchemaProto schemaProto = getSchemaProtoLocked();
@@ -258,6 +265,9 @@ public final class AppSearchImpl implements Closeable {
getAllNamespacesResultProto.getNamespacesCount(),
getAllNamespacesResultProto);
+ StorageInfoProto storageInfoProto = getRawStorageInfoProto();
+
+ // Log the time it took to read the data that goes into the cache maps
if (initStatsBuilder != null) {
initStatsBuilder
.setStatusCode(
@@ -268,20 +278,27 @@ public final class AppSearchImpl implements Closeable {
(SystemClock.elapsedRealtime()
- prepareSchemaAndNamespacesLatencyStartMillis));
}
-
checkSuccess(getAllNamespacesResultProto.getStatus());
// Populate schema map
- for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) {
+ List<SchemaTypeConfigProto> schemaProtoTypesList = schemaProto.getTypesList();
+ for (int i = 0; i < schemaProtoTypesList.size(); i++) {
+ SchemaTypeConfigProto schema = schemaProtoTypesList.get(i);
String prefixedSchemaType = schema.getSchemaType();
addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType), schema);
}
// Populate namespace map
- for (String prefixedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
+ List<String> prefixedNamespaceList =
+ getAllNamespacesResultProto.getNamespacesList();
+ for (int i = 0; i < prefixedNamespaceList.size(); i++) {
+ String prefixedNamespace = prefixedNamespaceList.get(i);
addToMap(mNamespaceMapLocked, getPrefix(prefixedNamespace), prefixedNamespace);
}
+ // Populate document count map
+ rebuildDocumentCountMapLocked(storageInfoProto);
+
// logging prepare_schema_and_namespaces latency
if (initStatsBuilder != null) {
initStatsBuilder.setPrepareSchemaAndNamespacesLatencyMillis(
@@ -596,10 +613,19 @@ public final class AppSearchImpl implements Closeable {
long rewriteDocumentTypeEndTimeMillis = SystemClock.elapsedRealtime();
DocumentProto finalDocument = documentBuilder.build();
+ // Check limits
+ int newDocumentCount =
+ enforceLimitConfigLocked(
+ packageName, finalDocument.getUri(), finalDocument.getSerializedSize());
+
+ // Insert document
mLogUtil.piiTrace("putDocument, request", finalDocument.getUri(), finalDocument);
- PutResultProto putResultProto = mIcingSearchEngineLocked.put(documentBuilder.build());
+ PutResultProto putResultProto = mIcingSearchEngineLocked.put(finalDocument);
mLogUtil.piiTrace("putDocument, response", putResultProto.getStatus(), putResultProto);
- addToMap(mNamespaceMapLocked, prefix, documentBuilder.getNamespace());
+
+ // Update caches
+ addToMap(mNamespaceMapLocked, prefix, finalDocument.getNamespace());
+ mDocumentCountMapLocked.put(packageName, newDocumentCount);
// Logging stats
if (pStatsBuilder != null) {
@@ -631,6 +657,71 @@ public final class AppSearchImpl implements Closeable {
}
/**
+ * Checks that a new document can be added to the given packageName with the given serialized
+ * size without violating our {@link LimitConfig}.
+ *
+ * @return the new count of documents for the given package, including the new document.
+ * @throws AppSearchException with a code of {@link AppSearchResult#RESULT_OUT_OF_SPACE} if the
+ * limits are violated by the new document.
+ */
+ @GuardedBy("mReadWriteLock")
+ private int enforceLimitConfigLocked(String packageName, String newDocUri, int newDocSize)
+ throws AppSearchException {
+ // Limits check: size of document
+ if (newDocSize > mLimitConfig.getMaxDocumentSizeBytes()) {
+ throw new AppSearchException(
+ AppSearchResult.RESULT_OUT_OF_SPACE,
+ "Document \""
+ + newDocUri
+ + "\" for package \""
+ + packageName
+ + "\" serialized to "
+ + newDocSize
+ + " bytes, which exceeds "
+ + "limit of "
+ + mLimitConfig.getMaxDocumentSizeBytes()
+ + " bytes");
+ }
+
+ // Limits check: number of documents
+ Integer oldDocumentCount = mDocumentCountMapLocked.get(packageName);
+ int newDocumentCount;
+ if (oldDocumentCount == null) {
+ newDocumentCount = 1;
+ } else {
+ newDocumentCount = oldDocumentCount + 1;
+ }
+ if (newDocumentCount > mLimitConfig.getMaxDocumentCount()) {
+ // Our management of mDocumentCountMapLocked doesn't account for document
+ // replacements, so our counter might have overcounted if the app has replaced docs.
+ // Rebuild the counter from StorageInfo in case this is so.
+ // TODO(b/170371356): If Icing lib exposes something in the result which says
+ // whether the document was a replacement, we could subtract 1 again after the put
+ // to keep the count accurate. That would allow us to remove this code.
+ rebuildDocumentCountMapLocked(getRawStorageInfoProto());
+ oldDocumentCount = mDocumentCountMapLocked.get(packageName);
+ if (oldDocumentCount == null) {
+ newDocumentCount = 1;
+ } else {
+ newDocumentCount = oldDocumentCount + 1;
+ }
+ }
+ if (newDocumentCount > mLimitConfig.getMaxDocumentCount()) {
+ // Now we really can't fit it in, even accounting for replacements.
+ throw new AppSearchException(
+ AppSearchResult.RESULT_OUT_OF_SPACE,
+ "Package \""
+ + packageName
+ + "\" exceeded limit of "
+ + mLimitConfig.getMaxDocumentCount()
+ + " documents. Some documents "
+ + "must be removed to index additional ones.");
+ }
+
+ return newDocumentCount;
+ }
+
+ /**
* Retrieves a document from the AppSearch index by namespace and document ID.
*
* <p>This method belongs to query group.
@@ -1121,6 +1212,9 @@ public final class AppSearchImpl implements Closeable {
deleteResultProto.getDeleteStats(), removeStatsBuilder);
}
checkSuccess(deleteResultProto.getStatus());
+
+ // Update derived maps
+ updateDocumentCountAfterRemovalLocked(packageName, /*numDocumentsDeleted=*/ 1);
} finally {
mReadWriteLock.writeLock().unlock();
if (removeStatsBuilder != null) {
@@ -1196,6 +1290,11 @@ public final class AppSearchImpl implements Closeable {
// not in the DB because it was not there or was successfully deleted.
checkCodeOneOf(
deleteResultProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND);
+
+ // Update derived maps
+ int numDocumentsDeleted =
+ deleteResultProto.getDeleteStats().getNumDocumentsDeleted();
+ updateDocumentCountAfterRemovalLocked(packageName, numDocumentsDeleted);
} finally {
mReadWriteLock.writeLock().unlock();
if (removeStatsBuilder != null) {
@@ -1205,6 +1304,22 @@ public final class AppSearchImpl implements Closeable {
}
}
+ @GuardedBy("mReadWriteLock")
+ private void updateDocumentCountAfterRemovalLocked(
+ @NonNull String packageName, int numDocumentsDeleted) {
+ if (numDocumentsDeleted > 0) {
+ Integer oldDocumentCount = mDocumentCountMapLocked.get(packageName);
+ // This should always be true: how can we delete documents for a package without
+ // having seen that package during init? This is just a safeguard.
+ if (oldDocumentCount != null) {
+ // This should always be >0; how can we remove more documents than we've indexed?
+ // This is just a safeguard.
+ int newDocumentCount = Math.max(oldDocumentCount - numDocumentsDeleted, 0);
+ mDocumentCountMapLocked.put(packageName, newDocumentCount);
+ }
+ }
+ }
+
/** Estimates the storage usage info for a specific package. */
@NonNull
public StorageInfo getStorageInfoForPackage(@NonNull String packageName)
@@ -1233,7 +1348,7 @@ public final class AppSearchImpl implements Closeable {
return new StorageInfo.Builder().build();
}
- return getStorageInfoForNamespacesLocked(wantedPrefixedNamespaces);
+ return getStorageInfoForNamespaces(getRawStorageInfoProto(), wantedPrefixedNamespaces);
} finally {
mReadWriteLock.readLock().unlock();
}
@@ -1264,29 +1379,45 @@ public final class AppSearchImpl implements Closeable {
return new StorageInfo.Builder().build();
}
- return getStorageInfoForNamespacesLocked(wantedPrefixedNamespaces);
+ return getStorageInfoForNamespaces(getRawStorageInfoProto(), wantedPrefixedNamespaces);
} finally {
mReadWriteLock.readLock().unlock();
}
}
- @GuardedBy("mReadWriteLock")
+ /**
+ * Returns the native storage info capsuled in {@link StorageInfoResultProto} directly from
+ * IcingSearchEngine.
+ */
@NonNull
- private StorageInfo getStorageInfoForNamespacesLocked(@NonNull Set<String> prefixedNamespaces)
- throws AppSearchException {
- mLogUtil.piiTrace("getStorageInfo, request");
- StorageInfoResultProto storageInfoResult = mIcingSearchEngineLocked.getStorageInfo();
- mLogUtil.piiTrace(
- "getStorageInfo, response", storageInfoResult.getStatus(), storageInfoResult);
- checkSuccess(storageInfoResult.getStatus());
- if (!storageInfoResult.hasStorageInfo()
- || !storageInfoResult.getStorageInfo().hasDocumentStorageInfo()) {
+ public StorageInfoProto getRawStorageInfoProto() throws AppSearchException {
+ mReadWriteLock.readLock().lock();
+ try {
+ throwIfClosedLocked();
+ mLogUtil.piiTrace("getStorageInfo, request");
+ StorageInfoResultProto storageInfoResult = mIcingSearchEngineLocked.getStorageInfo();
+ mLogUtil.piiTrace(
+ "getStorageInfo, response", storageInfoResult.getStatus(), storageInfoResult);
+ checkSuccess(storageInfoResult.getStatus());
+ return storageInfoResult.getStorageInfo();
+ } finally {
+ mReadWriteLock.readLock().unlock();
+ }
+ }
+
+ /**
+ * Extracts and returns {@link StorageInfo} from {@link StorageInfoProto} based on prefixed
+ * namespaces.
+ */
+ @NonNull
+ private static StorageInfo getStorageInfoForNamespaces(
+ @NonNull StorageInfoProto storageInfoProto, @NonNull Set<String> prefixedNamespaces) {
+ if (!storageInfoProto.hasDocumentStorageInfo()) {
return new StorageInfo.Builder().build();
}
- long totalStorageSize = storageInfoResult.getStorageInfo().getTotalStorageSize();
- DocumentStorageInfoProto documentStorageInfo =
- storageInfoResult.getStorageInfo().getDocumentStorageInfo();
+ long totalStorageSize = storageInfoProto.getTotalStorageSize();
+ DocumentStorageInfoProto documentStorageInfo = storageInfoProto.getDocumentStorageInfo();
int totalDocuments =
documentStorageInfo.getNumAliveDocuments()
+ documentStorageInfo.getNumExpiredDocuments();
@@ -1436,6 +1567,7 @@ public final class AppSearchImpl implements Closeable {
String packageName = entry.getKey();
Set<String> databaseNames = entry.getValue();
if (!installedPackages.contains(packageName) && databaseNames != null) {
+ mDocumentCountMapLocked.remove(packageName);
for (String databaseName : databaseNames) {
String removedPrefix = createPrefix(packageName, databaseName);
mSchemaMapLocked.remove(removedPrefix);
@@ -1468,6 +1600,7 @@ public final class AppSearchImpl implements Closeable {
mOptimizeIntervalCountLocked = 0;
mSchemaMapLocked.clear();
mNamespaceMapLocked.clear();
+ mDocumentCountMapLocked.clear();
if (initStatsBuilder != null) {
initStatsBuilder
.setHasReset(true)
@@ -1477,6 +1610,26 @@ public final class AppSearchImpl implements Closeable {
checkSuccess(resetResultProto.getStatus());
}
+ @GuardedBy("mReadWriteLock")
+ private void rebuildDocumentCountMapLocked(@NonNull StorageInfoProto storageInfoProto) {
+ mDocumentCountMapLocked.clear();
+ List<NamespaceStorageInfoProto> namespaceStorageInfoProtoList =
+ storageInfoProto.getDocumentStorageInfo().getNamespaceStorageInfoList();
+ for (int i = 0; i < namespaceStorageInfoProtoList.size(); i++) {
+ NamespaceStorageInfoProto namespaceStorageInfoProto =
+ namespaceStorageInfoProtoList.get(i);
+ String packageName = getPackageName(namespaceStorageInfoProto.getNamespace());
+ Integer oldCount = mDocumentCountMapLocked.get(packageName);
+ int newCount;
+ if (oldCount == null) {
+ newCount = namespaceStorageInfoProto.getNumAliveDocuments();
+ } else {
+ newCount = oldCount + namespaceStorageInfoProto.getNumAliveDocuments();
+ }
+ mDocumentCountMapLocked.put(packageName, newCount);
+ }
+ }
+
/** Wrapper around schema changes */
@VisibleForTesting
static class RewrittenSchemaResults {
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/LimitConfig.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/LimitConfig.java
new file mode 100644
index 000000000000..3f5723ee53e0
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/LimitConfig.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+
+/**
+ * Defines limits placed on users of AppSearch and enforced by {@link AppSearchImpl}.
+ *
+ * @hide
+ */
+public interface LimitConfig {
+ /**
+ * The maximum number of bytes a single document is allowed to be.
+ *
+ * <p>Enforced at the time of serializing the document into a proto.
+ *
+ * <p>This limit has two purposes:
+ *
+ * <ol>
+ * <li>Prevent the system service from using too much memory during indexing or querying by
+ * capping the size of the data structures it needs to buffer
+ * <li>Prevent apps from using a very large amount of data by storing exceptionally large
+ * documents.
+ * </ol>
+ */
+ int getMaxDocumentSizeBytes();
+
+ /**
+ * The maximum number of documents a single app is allowed to index.
+ *
+ * <p>Enforced at indexing time.
+ *
+ * <p>This limit has two purposes:
+ *
+ * <ol>
+ * <li>Protect icing lib's docid space from being overwhelmed by a single app. The overall
+ * docid limit is currently 2^20 (~1 million)
+ * <li>Prevent apps from using a very large amount of data on the system by storing too many
+ * documents.
+ * </ol>
+ */
+ int getMaxDocumentCount();
+}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/UnlimitedLimitConfig.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/UnlimitedLimitConfig.java
new file mode 100644
index 000000000000..0fabab04048b
--- /dev/null
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/UnlimitedLimitConfig.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+
+/**
+ * In Jetpack, AppSearch doesn't enforce artificial limits on number of documents or size of
+ * documents, since the app is the only user of the Icing instance. Icing still enforces a docid
+ * limit of 1M docs.
+ *
+ * @hide
+ */
+public class UnlimitedLimitConfig implements LimitConfig {
+ @Override
+ public int getMaxDocumentSizeBytes() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public int getMaxDocumentCount() {
+ return Integer.MAX_VALUE;
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java
index c4860287c567..ffb1dd9c7e78 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java
@@ -50,6 +50,10 @@ public class AppSearchConfigTest {
AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo(
AppSearchConfig.DEFAULT_SAMPLING_INTERVAL);
+ assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentSizeBytes()).isEqualTo(
+ AppSearchConfig.DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES);
+ assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentCount()).isEqualTo(
+ AppSearchConfig.DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT);
}
@Test
@@ -265,6 +269,22 @@ public class AppSearchConfigTest {
}
@Test
+ public void testCustomizedValue() {
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES,
+ Integer.toString(2001),
+ /*makeDefault=*/ false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT,
+ Integer.toString(2002),
+ /*makeDefault=*/ false);
+
+ AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
+ assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentSizeBytes()).isEqualTo(2001);
+ assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentCount()).isEqualTo(2002);
+ }
+
+ @Test
public void testNotUsable_afterClose() {
AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/FrameworkLimitConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/FrameworkLimitConfigTest.java
new file mode 100644
index 000000000000..088ed277aa80
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/appsearch/FrameworkLimitConfigTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 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;
+
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.provider.DeviceConfig;
+
+import com.android.server.testables.TestableDeviceConfig;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Tests for {@link FrameworkLimitConfig}.
+ *
+ * <p>Build/Install/Run: atest FrameworksMockingServicesTests:AppSearchConfigTest
+ */
+public class FrameworkLimitConfigTest {
+ @Rule
+ public final TestableDeviceConfig.TestableDeviceConfigRule
+ mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
+
+ @Test
+ public void testDefaultValues() {
+ AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
+ FrameworkLimitConfig config = new FrameworkLimitConfig(appSearchConfig);
+ assertThat(config.getMaxDocumentSizeBytes()).isEqualTo(
+ AppSearchConfig.DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES);
+ assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentCount()).isEqualTo(
+ AppSearchConfig.DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT);
+ }
+
+ @Test
+ public void testCustomizedValues() {
+ AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
+ FrameworkLimitConfig config = new FrameworkLimitConfig(appSearchConfig);
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES,
+ "2001",
+ /*makeDefault=*/ false);
+ DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_APPSEARCH,
+ AppSearchConfig.KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT,
+ "2002",
+ /*makeDefault=*/ false);
+
+ assertThat(config.getMaxDocumentSizeBytes()).isEqualTo(2001);
+ assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentCount()).isEqualTo(2002);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
index 3c10789bc792..0d475c00569e 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
@@ -92,7 +92,10 @@ public class AppSearchImplPlatformTest {
// Give ourselves global query permissions
mAppSearchImpl = AppSearchImpl.create(
- mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ mTemporaryFolder.newFolder(),
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
mVisibilityStore = VisibilityStoreImpl.create(mAppSearchImpl, mContext);
mGlobalQuerierUid =
mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
index 330b1a74d879..91f49224fde8 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
@@ -54,6 +54,7 @@ import com.android.server.appsearch.icing.proto.SchemaTypeConfigProto;
import com.android.server.appsearch.icing.proto.SearchResultProto;
import com.android.server.appsearch.icing.proto.SearchSpecProto;
import com.android.server.appsearch.icing.proto.StatusProto;
+import com.android.server.appsearch.icing.proto.StorageInfoProto;
import com.android.server.appsearch.icing.proto.StringIndexingConfig;
import com.android.server.appsearch.icing.proto.TermMatchType;
@@ -85,7 +86,10 @@ public class AppSearchImplTest {
public void setUp() throws Exception {
mAppSearchImpl =
AppSearchImpl.create(
- mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ mTemporaryFolder.newFolder(),
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
}
/**
@@ -468,7 +472,11 @@ public class AppSearchImplTest {
Context context = ApplicationProvider.getApplicationContext();
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
- AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(
+ appsearchDir,
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
// Insert schema
List<AppSearchSchema> schemas =
@@ -529,7 +537,12 @@ public class AppSearchImplTest {
// Initialize AppSearchImpl. This should cause a reset.
InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
appSearchImpl.close();
- appSearchImpl = AppSearchImpl.create(appsearchDir, initStatsBuilder, ALWAYS_OPTIMIZE);
+ appSearchImpl =
+ AppSearchImpl.create(
+ appsearchDir,
+ new UnlimitedLimitConfig(),
+ initStatsBuilder,
+ ALWAYS_OPTIMIZE);
// Check recovery state
InitializeStats initStats = initStatsBuilder.build();
@@ -1688,7 +1701,10 @@ public class AppSearchImplTest {
public void testThrowsExceptionIfClosed() throws Exception {
AppSearchImpl appSearchImpl =
AppSearchImpl.create(
- mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ mTemporaryFolder.newFolder(),
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
// Initial check that we could do something at first.
List<AppSearchSchema> schemas =
@@ -1816,7 +1832,11 @@ public class AppSearchImplTest {
// Setup the index
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
- AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(
+ appsearchDir,
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1843,7 +1863,11 @@ public class AppSearchImplTest {
// That document should be visible even from another instance.
AppSearchImpl appSearchImpl2 =
- AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(
+ appsearchDir,
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
getResult =
appSearchImpl2.getDocument(
"package", "database", "namespace1", "id1", Collections.emptyMap());
@@ -1855,7 +1879,11 @@ public class AppSearchImplTest {
// Setup the index
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
- AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(
+ appsearchDir,
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1906,7 +1934,11 @@ public class AppSearchImplTest {
// Only the second document should be retrievable from another instance.
AppSearchImpl appSearchImpl2 =
- AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(
+ appsearchDir,
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
assertThrows(
AppSearchException.class,
() ->
@@ -1927,7 +1959,11 @@ public class AppSearchImplTest {
// Setup the index
File appsearchDir = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
- AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(
+ appsearchDir,
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
@@ -1986,7 +2022,11 @@ public class AppSearchImplTest {
// Only the second document should be retrievable from another instance.
AppSearchImpl appSearchImpl2 =
- AppSearchImpl.create(appsearchDir, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(
+ appsearchDir,
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
assertThrows(
AppSearchException.class,
() ->
@@ -2001,4 +2041,784 @@ public class AppSearchImplTest {
"package", "database", "namespace2", "id2", Collections.emptyMap());
assertThat(getResult).isEqualTo(document2);
}
+
+ @Test
+ public void testGetIcingSearchEngineStorageInfo() throws Exception {
+ // Setup the index
+ File appsearchDir = mTemporaryFolder.newFolder();
+ AppSearchImpl appSearchImpl =
+ AppSearchImpl.create(
+ appsearchDir,
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ appSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Add two documents
+ GenericDocument document1 =
+ new GenericDocument.Builder<>("namespace1", "id1", "type").build();
+ appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null);
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace1", "id2", "type").build();
+ appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null);
+
+ StorageInfoProto storageInfo = appSearchImpl.getRawStorageInfoProto();
+
+ // Simple checks to verify if we can get correct StorageInfoProto from IcingSearchEngine
+ // No need to cover all the fields
+ assertThat(storageInfo.getTotalStorageSize()).isGreaterThan(0);
+ assertThat(storageInfo.getDocumentStorageInfo().getNumAliveDocuments()).isEqualTo(2);
+ assertThat(storageInfo.getSchemaStoreStorageInfo().getNumSchemaTypes()).isEqualTo(1);
+ }
+
+ @Test
+ public void testLimitConfig_DocumentSize() throws Exception {
+ // Create a new mAppSearchImpl with a lower limit
+ mAppSearchImpl.close();
+ mAppSearchImpl =
+ AppSearchImpl.create(
+ mTemporaryFolder.newFolder(),
+ new LimitConfig() {
+ @Override
+ public int getMaxDocumentSizeBytes() {
+ return 80;
+ }
+
+ @Override
+ public int getMaxDocumentCount() {
+ return 1;
+ }
+ },
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ // Insert schema
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ mAppSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Insert a document which is too large
+ GenericDocument document =
+ new GenericDocument.Builder<>(
+ "this_namespace_is_long_to_make_the_doc_big", "id", "type")
+ .build();
+ AppSearchException e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package", "database", document, /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains(
+ "Document \"id\" for package \"package\" serialized to 99 bytes, which"
+ + " exceeds limit of 80 bytes");
+
+ // Make sure this failure didn't increase our document count. We should still be able to
+ // index 1 document.
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace", "id2", "type").build();
+ mAppSearchImpl.putDocument("package", "database", document2, /*logger=*/ null);
+
+ // Now we should get a failure
+ GenericDocument document3 =
+ new GenericDocument.Builder<>("namespace", "id3", "type").build();
+ e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package", "database", document3, /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package\" exceeded limit of 1 documents");
+ }
+
+ @Test
+ public void testLimitConfig_Init() throws Exception {
+ // Create a new mAppSearchImpl with a lower limit
+ mAppSearchImpl.close();
+ File tempFolder = mTemporaryFolder.newFolder();
+ mAppSearchImpl =
+ AppSearchImpl.create(
+ tempFolder,
+ new LimitConfig() {
+ @Override
+ public int getMaxDocumentSizeBytes() {
+ return 80;
+ }
+
+ @Override
+ public int getMaxDocumentCount() {
+ return 1;
+ }
+ },
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ // Insert schema
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ mAppSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Index a document
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id1", "type").build(),
+ /*logger=*/ null);
+
+ // Now we should get a failure
+ GenericDocument document2 =
+ new GenericDocument.Builder<>("namespace", "id2", "type").build();
+ AppSearchException e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package", "database", document2, /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package\" exceeded limit of 1 documents");
+
+ // Close and reinitialize AppSearchImpl
+ mAppSearchImpl.close();
+ mAppSearchImpl =
+ AppSearchImpl.create(
+ tempFolder,
+ new LimitConfig() {
+ @Override
+ public int getMaxDocumentSizeBytes() {
+ return 80;
+ }
+
+ @Override
+ public int getMaxDocumentCount() {
+ return 1;
+ }
+ },
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ // Make sure the limit is maintained
+ e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package", "database", document2, /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package\" exceeded limit of 1 documents");
+ }
+
+ @Test
+ public void testLimitConfig_Remove() throws Exception {
+ // Create a new mAppSearchImpl with a lower limit
+ mAppSearchImpl.close();
+ mAppSearchImpl =
+ AppSearchImpl.create(
+ mTemporaryFolder.newFolder(),
+ new LimitConfig() {
+ @Override
+ public int getMaxDocumentSizeBytes() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public int getMaxDocumentCount() {
+ return 3;
+ }
+ },
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ // Insert schema
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ mAppSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Index 3 documents
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id1", "type").build(),
+ /*logger=*/ null);
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id2", "type").build(),
+ /*logger=*/ null);
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id3", "type").build(),
+ /*logger=*/ null);
+
+ // Now we should get a failure
+ GenericDocument document4 =
+ new GenericDocument.Builder<>("namespace", "id4", "type").build();
+ AppSearchException e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package", "database", document4, /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package\" exceeded limit of 3 documents");
+
+ // Remove a document that doesn't exist
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.remove(
+ "package",
+ "database",
+ "namespace",
+ "id4",
+ /*removeStatsBuilder=*/ null));
+
+ // Should still fail
+ e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package", "database", document4, /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package\" exceeded limit of 3 documents");
+
+ // Remove a document that does exist
+ mAppSearchImpl.remove(
+ "package", "database", "namespace", "id2", /*removeStatsBuilder=*/ null);
+
+ // Now doc4 should work
+ mAppSearchImpl.putDocument("package", "database", document4, /*logger=*/ null);
+
+ // The next one should fail again
+ e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id5", "type")
+ .build(),
+ /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package\" exceeded limit of 3 documents");
+ }
+
+ @Test
+ public void testLimitConfig_DifferentPackages() throws Exception {
+ // Create a new mAppSearchImpl with a lower limit
+ mAppSearchImpl.close();
+ File tempFolder = mTemporaryFolder.newFolder();
+ mAppSearchImpl =
+ AppSearchImpl.create(
+ tempFolder,
+ new LimitConfig() {
+ @Override
+ public int getMaxDocumentSizeBytes() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public int getMaxDocumentCount() {
+ return 2;
+ }
+ },
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ // Insert schema
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(new AppSearchSchema.Builder("type").build());
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database1",
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+ mAppSearchImpl.setSchema(
+ "package1",
+ "database2",
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+ mAppSearchImpl.setSchema(
+ "package2",
+ "database1",
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+ mAppSearchImpl.setSchema(
+ "package2",
+ "database2",
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Index documents in package1/database1
+ mAppSearchImpl.putDocument(
+ "package1",
+ "database1",
+ new GenericDocument.Builder<>("namespace", "id1", "type").build(),
+ /*logger=*/ null);
+ mAppSearchImpl.putDocument(
+ "package1",
+ "database2",
+ new GenericDocument.Builder<>("namespace", "id2", "type").build(),
+ /*logger=*/ null);
+
+ // Indexing a third doc into package1 should fail (here we use database3)
+ AppSearchException e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package1",
+ "database3",
+ new GenericDocument.Builder<>("namespace", "id3", "type")
+ .build(),
+ /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package1\" exceeded limit of 2 documents");
+
+ // Indexing a doc into package2 should succeed
+ mAppSearchImpl.putDocument(
+ "package2",
+ "database1",
+ new GenericDocument.Builder<>("namespace", "id1", "type").build(),
+ /*logger=*/ null);
+
+ // Reinitialize to make sure packages are parsed correctly on init
+ mAppSearchImpl.close();
+ mAppSearchImpl =
+ AppSearchImpl.create(
+ tempFolder,
+ new LimitConfig() {
+ @Override
+ public int getMaxDocumentSizeBytes() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public int getMaxDocumentCount() {
+ return 2;
+ }
+ },
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ // package1 should still be out of space
+ e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package1",
+ "database4",
+ new GenericDocument.Builder<>("namespace", "id4", "type")
+ .build(),
+ /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package1\" exceeded limit of 2 documents");
+
+ // package2 has room for one more
+ mAppSearchImpl.putDocument(
+ "package2",
+ "database2",
+ new GenericDocument.Builder<>("namespace", "id2", "type").build(),
+ /*logger=*/ null);
+
+ // now package2 really is out of space
+ e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package2",
+ "database3",
+ new GenericDocument.Builder<>("namespace", "id3", "type")
+ .build(),
+ /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package2\" exceeded limit of 2 documents");
+ }
+
+ @Test
+ public void testLimitConfig_RemoveByQyery() throws Exception {
+ // Create a new mAppSearchImpl with a lower limit
+ mAppSearchImpl.close();
+ mAppSearchImpl =
+ AppSearchImpl.create(
+ mTemporaryFolder.newFolder(),
+ new LimitConfig() {
+ @Override
+ public int getMaxDocumentSizeBytes() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public int getMaxDocumentCount() {
+ return 3;
+ }
+ },
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ // Insert schema
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(
+ new AppSearchSchema.Builder("type")
+ .addProperty(
+ new AppSearchSchema.StringPropertyConfig.Builder("body")
+ .setIndexingType(
+ AppSearchSchema.StringPropertyConfig
+ .INDEXING_TYPE_PREFIXES)
+ .setTokenizerType(
+ AppSearchSchema.StringPropertyConfig
+ .TOKENIZER_TYPE_PLAIN)
+ .build())
+ .build());
+ mAppSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Index 3 documents
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id1", "type")
+ .setPropertyString("body", "tablet")
+ .build(),
+ /*logger=*/ null);
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id2", "type")
+ .setPropertyString("body", "tabby")
+ .build(),
+ /*logger=*/ null);
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id3", "type")
+ .setPropertyString("body", "grabby")
+ .build(),
+ /*logger=*/ null);
+
+ // Now we should get a failure
+ GenericDocument document4 =
+ new GenericDocument.Builder<>("namespace", "id4", "type").build();
+ AppSearchException e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package", "database", document4, /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package\" exceeded limit of 3 documents");
+
+ // Run removebyquery, deleting nothing
+ mAppSearchImpl.removeByQuery(
+ "package",
+ "database",
+ "nothing",
+ new SearchSpec.Builder().build(),
+ /*removeStatsBuilder=*/ null);
+
+ // Should still fail
+ e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package", "database", document4, /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package\" exceeded limit of 3 documents");
+
+ // Remove "tab*"
+ mAppSearchImpl.removeByQuery(
+ "package",
+ "database",
+ "tab",
+ new SearchSpec.Builder().build(),
+ /*removeStatsBuilder=*/ null);
+
+ // Now doc4 and doc5 should work
+ mAppSearchImpl.putDocument("package", "database", document4, /*logger=*/ null);
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id5", "type").build(),
+ /*logger=*/ null);
+
+ // We only deleted 2 docs so the next one should fail again
+ e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id6", "type")
+ .build(),
+ /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package\" exceeded limit of 3 documents");
+ }
+
+ @Test
+ public void testLimitConfig_Replace() throws Exception {
+ // Create a new mAppSearchImpl with a lower limit
+ mAppSearchImpl.close();
+ mAppSearchImpl =
+ AppSearchImpl.create(
+ mTemporaryFolder.newFolder(),
+ new LimitConfig() {
+ @Override
+ public int getMaxDocumentSizeBytes() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public int getMaxDocumentCount() {
+ return 2;
+ }
+ },
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ // Insert schema
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(
+ new AppSearchSchema.Builder("type")
+ .addProperty(
+ new AppSearchSchema.StringPropertyConfig.Builder("body")
+ .build())
+ .build());
+ mAppSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Index a document
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id1", "type")
+ .setPropertyString("body", "id1.orig")
+ .build(),
+ /*logger=*/ null);
+ // Replace it with another doc
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id1", "type")
+ .setPropertyString("body", "id1.new")
+ .build(),
+ /*logger=*/ null);
+
+ // Index id2. This should pass but only because we check for replacements.
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id2", "type").build(),
+ /*logger=*/ null);
+
+ // Now we should get a failure on id3
+ GenericDocument document3 =
+ new GenericDocument.Builder<>("namespace", "id3", "type").build();
+ AppSearchException e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package", "database", document3, /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package\" exceeded limit of 2 documents");
+ }
+
+ @Test
+ public void testLimitConfig_ReplaceReinit() throws Exception {
+ // Create a new mAppSearchImpl with a lower limit
+ mAppSearchImpl.close();
+ File tempFolder = mTemporaryFolder.newFolder();
+ mAppSearchImpl =
+ AppSearchImpl.create(
+ tempFolder,
+ new LimitConfig() {
+ @Override
+ public int getMaxDocumentSizeBytes() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public int getMaxDocumentCount() {
+ return 2;
+ }
+ },
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ // Insert schema
+ List<AppSearchSchema> schemas =
+ Collections.singletonList(
+ new AppSearchSchema.Builder("type")
+ .addProperty(
+ new AppSearchSchema.StringPropertyConfig.Builder("body")
+ .build())
+ .build());
+ mAppSearchImpl.setSchema(
+ "package",
+ "database",
+ schemas,
+ /*visibilityStore=*/ null,
+ /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
+ /*schemasVisibleToPackages=*/ Collections.emptyMap(),
+ /*forceOverride=*/ false,
+ /*version=*/ 0);
+
+ // Index a document
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id1", "type")
+ .setPropertyString("body", "id1.orig")
+ .build(),
+ /*logger=*/ null);
+ // Replace it with another doc
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id1", "type")
+ .setPropertyString("body", "id1.new")
+ .build(),
+ /*logger=*/ null);
+
+ // Reinitialize to make sure replacements are correctly accounted for by init
+ mAppSearchImpl.close();
+ mAppSearchImpl =
+ AppSearchImpl.create(
+ tempFolder,
+ new LimitConfig() {
+ @Override
+ public int getMaxDocumentSizeBytes() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ public int getMaxDocumentCount() {
+ return 2;
+ }
+ },
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
+
+ // Index id2. This should pass but only because we check for replacements.
+ mAppSearchImpl.putDocument(
+ "package",
+ "database",
+ new GenericDocument.Builder<>("namespace", "id2", "type").build(),
+ /*logger=*/ null);
+
+ // Now we should get a failure on id3
+ GenericDocument document3 =
+ new GenericDocument.Builder<>("namespace", "id3", "type").build();
+ AppSearchException e =
+ assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ "package", "database", document3, /*logger=*/ null));
+ assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
+ assertThat(e)
+ .hasMessageThat()
+ .contains("Package \"package\" exceeded limit of 2 documents");
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
index 080c375ac7c6..7bacbb63f10c 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
@@ -67,7 +67,10 @@ public class AppSearchLoggerTest {
public void setUp() throws Exception {
mAppSearchImpl =
AppSearchImpl.create(
- mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ mTemporaryFolder.newFolder(),
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
mLogger = new TestLogger();
}
@@ -290,7 +293,11 @@ public class AppSearchLoggerTest {
public void testLoggingStats_initializeWithoutDocuments_success() throws Exception {
// Create an unused AppSearchImpl to generated an InitializeStats.
InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
- AppSearchImpl.create(mTemporaryFolder.newFolder(), initStatsBuilder, ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(
+ mTemporaryFolder.newFolder(),
+ new UnlimitedLimitConfig(),
+ initStatsBuilder,
+ ALWAYS_OPTIMIZE);
InitializeStats iStats = initStatsBuilder.build();
assertThat(iStats).isNotNull();
@@ -314,7 +321,11 @@ public class AppSearchLoggerTest {
final File folder = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
- AppSearchImpl.create(folder, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(
+ folder,
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
List<AppSearchSchema> schemas =
ImmutableList.of(
new AppSearchSchema.Builder("Type1").build(),
@@ -336,7 +347,7 @@ public class AppSearchLoggerTest {
// Create another appsearchImpl on the same folder
InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
- AppSearchImpl.create(folder, initStatsBuilder, ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(folder, new UnlimitedLimitConfig(), initStatsBuilder, ALWAYS_OPTIMIZE);
InitializeStats iStats = initStatsBuilder.build();
assertThat(iStats).isNotNull();
@@ -360,7 +371,11 @@ public class AppSearchLoggerTest {
final File folder = mTemporaryFolder.newFolder();
AppSearchImpl appSearchImpl =
- AppSearchImpl.create(folder, /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(
+ folder,
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
List<AppSearchSchema> schemas =
ImmutableList.of(
@@ -393,7 +408,7 @@ public class AppSearchLoggerTest {
// Create another appsearchImpl on the same folder
InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder();
- AppSearchImpl.create(folder, initStatsBuilder, ALWAYS_OPTIMIZE);
+ AppSearchImpl.create(folder, new UnlimitedLimitConfig(), initStatsBuilder, ALWAYS_OPTIMIZE);
InitializeStats iStats = initStatsBuilder.build();
// Some of other fields are already covered by AppSearchImplTest#testReset()
@@ -484,11 +499,13 @@ public class AppSearchLoggerTest {
.setPropertyString("nonExist", "testPut example1")
.build();
- // We mainly want to check the status code in stats. So we don't need to inspect the
- // exception here.
- Assert.assertThrows(
- AppSearchException.class,
- () -> mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger));
+ AppSearchException exception =
+ Assert.assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.putDocument(
+ testPackageName, testDatabase, document, mLogger));
+ assertThat(exception.getResultCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
PutDocumentStats pStats = mLogger.mPutDocumentStats;
assertThat(pStats).isNotNull();
@@ -676,17 +693,17 @@ public class AppSearchLoggerTest {
RemoveStats.Builder rStatsBuilder = new RemoveStats.Builder(testPackageName, testDatabase);
- // We mainly want to check the status code in stats. So we don't need to inspect the
- // exception here.
- Assert.assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.remove(
- testPackageName,
- testDatabase,
- testNamespace,
- "invalidId",
- rStatsBuilder));
+ AppSearchException exception =
+ Assert.assertThrows(
+ AppSearchException.class,
+ () ->
+ mAppSearchImpl.remove(
+ testPackageName,
+ testDatabase,
+ testNamespace,
+ "invalidId",
+ rStatsBuilder));
+ assertThat(exception.getResultCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND);
RemoveStats rStats = rStatsBuilder.build();
assertThat(rStats.getPackageName()).isEqualTo(testPackageName);
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java
index 07a728bac2a5..374642b676d2 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java
@@ -38,6 +38,7 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.server.appsearch.external.localstorage.AppSearchImpl;
import com.android.server.appsearch.external.localstorage.OptimizeStrategy;
+import com.android.server.appsearch.external.localstorage.UnlimitedLimitConfig;
import com.android.server.appsearch.external.localstorage.util.PrefixUtil;
import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore;
@@ -88,7 +89,10 @@ public class VisibilityStoreImplTest {
// Give ourselves global query permissions
AppSearchImpl appSearchImpl = AppSearchImpl.create(
- mTemporaryFolder.newFolder(), /*initStatsBuilder=*/ null, ALWAYS_OPTIMIZE);
+ mTemporaryFolder.newFolder(),
+ new UnlimitedLimitConfig(),
+ /*initStatsBuilder=*/ null,
+ ALWAYS_OPTIMIZE);
mVisibilityStore = VisibilityStoreImpl.create(appSearchImpl, mContext);
mUid = mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0);
}