From d4f12c6ea6acd6e3007d83259c3f94fe0252d5d2 Mon Sep 17 00:00:00 2001 From: Baligh Uddin Date: Wed, 29 Sep 2021 17:17:53 +0000 Subject: [PATCH] Remove frameworks/base/apex/appsearch and associated tests BUG: 192108282 Test: TH Merged-In: I38469ad5df100e90aba81bbdc82078b568301f09 Change-Id: Ib7e97c6ee7dff1c5d0aad2c21635e74510d523a6 --- apex/appsearch/Android.bp | 75 - apex/appsearch/OWNERS | 3 - apex/appsearch/apex_manifest.json | 4 - .../appsearch/com.android.appsearch.avbpubkey | Bin 1032 -> 0 bytes apex/appsearch/com.android.appsearch.pem | 51 - apex/appsearch/com.android.appsearch.pk8 | Bin 2373 -> 0 bytes apex/appsearch/com.android.appsearch.x509.pem | 35 - apex/appsearch/framework/Android.bp | 68 - apex/appsearch/framework/api/current.txt | 460 --- .../framework/api/module-lib-current.txt | 1 - .../framework/api/module-lib-removed.txt | 1 - apex/appsearch/framework/api/removed.txt | 1 - .../framework/api/system-current.txt | 9 - .../framework/api/system-removed.txt | 1 - apex/appsearch/framework/jarjar-rules.txt | 6 - apex/appsearch/framework/java/TEST_MAPPING | 7 - .../app/appsearch/AppSearchManager.java | 242 -- .../AppSearchManagerFrameworkInitializer.java | 45 - .../appsearch/AppSearchMigrationHelper.java | 271 -- .../app/appsearch/AppSearchSession.java | 885 ----- .../app/appsearch/BatchResultCallback.java | 58 - .../app/appsearch/GlobalSearchSession.java | 200 - .../android/app/appsearch/SearchResults.java | 175 - .../aidl/AppSearchBatchResultParcel.aidl | 19 - .../aidl/AppSearchBatchResultParcel.java | 97 - .../appsearch/aidl/AppSearchResultParcel.aidl | 19 - .../appsearch/aidl/AppSearchResultParcel.java | 95 - .../aidl/IAppSearchBatchResultCallback.aidl | 25 - .../app/appsearch/aidl/IAppSearchManager.aidl | 371 -- .../aidl/IAppSearchResultCallback.aidl | 23 - .../app/appsearch/AppSearchBatchResult.java | 209 -- .../app/appsearch/AppSearchResult.java | 249 -- .../app/appsearch/AppSearchSchema.java | 922 ----- .../app/appsearch/GenericDocument.java | 1342 ------- .../app/appsearch/GetByDocumentIdRequest.java | 181 - .../app/appsearch/GetSchemaResponse.java | 118 - .../android/app/appsearch/Migrator.java | 91 - .../app/appsearch/PackageIdentifier.java | 81 - .../app/appsearch/PutDocumentsRequest.java | 84 - .../appsearch/RemoveByDocumentIdRequest.java | 97 - .../appsearch/ReportSystemUsageRequest.java | 145 - .../app/appsearch/ReportUsageRequest.java | 110 - .../android/app/appsearch/SearchResult.java | 549 --- .../app/appsearch/SearchResultPage.java | 74 - .../android/app/appsearch/SearchSpec.java | 676 ---- .../app/appsearch/SetSchemaRequest.java | 461 --- .../app/appsearch/SetSchemaResponse.java | 381 -- .../android/app/appsearch/StorageInfo.java | 110 - .../exceptions/AppSearchException.java | 85 - .../exceptions/IllegalSchemaException.java | 36 - .../app/appsearch/util/BundleUtil.java | 251 -- .../util/IndentingStringBuilder.java | 126 - .../android/app/appsearch/util/LogUtil.java | 104 - .../appsearch/util/SchemaMigrationUtil.java | 108 - apex/appsearch/service/Android.bp | 69 - apex/appsearch/service/jarjar-rules.txt | 12 - .../server/appsearch/AppSearchConfig.java | 454 --- .../appsearch/AppSearchManagerService.java | 1522 -------- .../appsearch/AppSearchUserInstance.java | 58 - .../AppSearchUserInstanceManager.java | 211 -- .../appsearch/FrameworkLimitConfig.java | 41 - .../appsearch/FrameworkOptimizeStrategy.java | 48 - .../com/android/server/appsearch/TEST_MAPPING | 26 - .../external/localstorage/AppSearchImpl.java | 2299 ------------ .../localstorage/AppSearchLogger.java | 58 - .../localstorage/AppSearchLoggerHelper.java | 167 - .../external/localstorage/LimitConfig.java | 57 - .../localstorage/OptimizeStrategy.java | 39 - .../localstorage/UnlimitedLimitConfig.java | 37 - .../GenericDocumentToProtoConverter.java | 221 -- .../converter/ResultCodeToProtoConverter.java | 62 - .../converter/SchemaToProtoConverter.java | 227 -- .../SearchResultToProtoConverter.java | 141 - .../converter/SearchSpecToProtoConverter.java | 116 - .../SetSchemaResponseToProtoConverter.java | 60 - .../TypePropertyPathToProtoConverter.java | 51 - .../localstorage/stats/CallStats.java | 262 -- .../localstorage/stats/InitializeStats.java | 442 --- .../localstorage/stats/OptimizeStats.java | 243 -- .../localstorage/stats/PutDocumentStats.java | 273 -- .../localstorage/stats/RemoveStats.java | 181 - .../stats/SchemaMigrationStats.java | 189 - .../localstorage/stats/SearchStats.java | 463 --- .../localstorage/stats/SetSchemaStats.java | 245 -- .../localstorage/util/PrefixUtil.java | 235 -- .../visibilitystore/VisibilityStore.java | 72 - .../appsearch/stats/PlatformLogger.java | 527 --- .../appsearch/stats/StatsCollector.java | 203 - .../server/appsearch/util/PackageUtil.java | 44 - .../NotDisplayedBySystemMap.java | 76 - .../visibilitystore/VisibilityDocument.java | 86 - .../visibilitystore/VisibilityStoreImpl.java | 352 -- .../VisibleToPackagesDocument.java | 105 - .../visibilitystore/VisibleToPackagesMap.java | 85 - apex/appsearch/synced_jetpack_changeid.txt | 1 - apex/appsearch/testing/Android.bp | 39 - .../testing/AppSearchSessionShimImpl.java | 229 -- .../testing/GlobalSearchSessionShimImpl.java | 104 - .../testing/SearchResultsShimImpl.java | 58 - .../testing/external/AppSearchEmail.java | 208 -- .../external/AppSearchSessionShim.java | 261 -- .../testing/external/AppSearchTestUtils.java | 94 - .../external/GlobalSearchSessionShim.java | 82 - .../testing/external/SearchResultsShim.java | 56 - .../appsearch/testing/package-info.java | 20 - .../appsearch/AppSearchSessionUnitTest.java | 230 -- .../src/android/app/appsearch/OWNERS | 1 - .../external/app/AppSearchEmailTest.java | 56 - .../external/app/AppSearchResultTest.java | 41 - .../external/app/GenericDocumentTest.java | 79 - .../external/app/SearchSpecTest.java | 60 - .../external/app/SetSchemaResponseTest.java | 73 - .../IllegalSchemaExceptionTest.java | 30 - .../external/util/BundleUtilTest.java | 277 -- .../util/IndentingStringBuilderTest.java | 89 - .../server/appsearch/AppSearchConfigTest.java | 474 --- .../appsearch/FrameworkLimitConfigTest.java | 68 - .../src/com/android/server/appsearch/OWNERS | 1 - .../appsearch/stats/PlatformLoggerTest.java | 228 -- .../appsearch/AppSearchImplPlatformTest.java | 454 --- .../FrameworkOptimizeStrategyTest.java | 70 - .../src/com/android/server/appsearch/OWNERS | 1 - .../localstorage/AppSearchImplTest.java | 3272 ----------------- .../localstorage/AppSearchLoggerTest.java | 806 ---- .../GenericDocumentToProtoConverterTest.java | 256 -- .../converter/SchemaToProtoConverterTest.java | 151 - .../localstorage/converter/SnippetTest.java | 322 -- .../stats/AppSearchStatsTest.java | 396 -- .../appsearch/stats/PlatformLoggerTest.java | 199 - .../VisibilityStoreImplTest.java | 469 --- 130 files changed, 29151 deletions(-) delete mode 100644 apex/appsearch/Android.bp delete mode 100644 apex/appsearch/OWNERS delete mode 100644 apex/appsearch/apex_manifest.json delete mode 100644 apex/appsearch/com.android.appsearch.avbpubkey delete mode 100644 apex/appsearch/com.android.appsearch.pem delete mode 100644 apex/appsearch/com.android.appsearch.pk8 delete mode 100644 apex/appsearch/com.android.appsearch.x509.pem delete mode 100644 apex/appsearch/framework/Android.bp delete mode 100644 apex/appsearch/framework/api/current.txt delete mode 100644 apex/appsearch/framework/api/module-lib-current.txt delete mode 100644 apex/appsearch/framework/api/module-lib-removed.txt delete mode 100644 apex/appsearch/framework/api/removed.txt delete mode 100644 apex/appsearch/framework/api/system-current.txt delete mode 100644 apex/appsearch/framework/api/system-removed.txt delete mode 100644 apex/appsearch/framework/jarjar-rules.txt delete mode 100644 apex/appsearch/framework/java/TEST_MAPPING delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/BatchResultCallback.java delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/SearchResults.java delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.aidl delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.java delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.aidl delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.java delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchBatchResultCallback.aidl delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl delete mode 100644 apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchResultCallback.aidl delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/Migrator.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/RemoveByDocumentIdRequest.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/StorageInfo.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSchemaException.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/util/IndentingStringBuilder.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/util/LogUtil.java delete mode 100644 apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java delete mode 100644 apex/appsearch/service/Android.bp delete mode 100644 apex/appsearch/service/jarjar-rules.txt delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/FrameworkLimitConfig.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/FrameworkOptimizeStrategy.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/LimitConfig.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/OptimizeStrategy.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/UnlimitedLimitConfig.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/ResultCodeToProtoConverter.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/OptimizeStats.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/RemoveStats.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SchemaMigrationStats.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/stats/StatsCollector.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/util/PackageUtil.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotDisplayedBySystemMap.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesDocument.java delete mode 100644 apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesMap.java delete mode 100644 apex/appsearch/synced_jetpack_changeid.txt delete mode 100644 apex/appsearch/testing/Android.bp delete mode 100644 apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java delete mode 100644 apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java delete mode 100644 apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java delete mode 100644 apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchEmail.java delete mode 100644 apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java delete mode 100644 apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java delete mode 100644 apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java delete mode 100644 apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java delete mode 100644 apex/appsearch/testing/java/com/android/server/appsearch/testing/package-info.java delete mode 100644 core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java delete mode 100644 core/tests/coretests/src/android/app/appsearch/OWNERS delete mode 100644 core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java delete mode 100644 core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java delete mode 100644 core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java delete mode 100644 core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java delete mode 100644 core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaResponseTest.java delete mode 100644 core/tests/coretests/src/android/app/appsearch/external/exceptions/IllegalSchemaExceptionTest.java delete mode 100644 core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java delete mode 100644 core/tests/coretests/src/android/app/appsearch/external/util/IndentingStringBuilderTest.java delete mode 100644 services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java delete mode 100644 services/tests/mockingservicestests/src/com/android/server/appsearch/FrameworkLimitConfigTest.java delete mode 100644 services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS delete mode 100644 services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/appsearch/FrameworkOptimizeStrategyTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/appsearch/OWNERS delete mode 100644 services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java delete mode 100644 services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java diff --git a/apex/appsearch/Android.bp b/apex/appsearch/Android.bp deleted file mode 100644 index 977e610d734f2..0000000000000 --- a/apex/appsearch/Android.bp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2019 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -apex { - name: "com.android.appsearch", - manifest: "apex_manifest.json", - bootclasspath_fragments: ["com.android.appsearch-bootclasspath-fragment"], - systemserverclasspath_fragments: ["com.android.appsearch-systemserverclasspath-fragment"], - key: "com.android.appsearch.key", - certificate: ":com.android.appsearch.certificate", - updatable: false, - jni_libs: ["libicing"], - generate_hashtree: false, -} - -apex_key { - name: "com.android.appsearch.key", - public_key: "com.android.appsearch.avbpubkey", - private_key: "com.android.appsearch.pem", -} - -android_app_certificate { - name: "com.android.appsearch.certificate", - // This will use com.android.appsearch.x509.pem (the cert) and - // com.android.appsearch.pk8 (the private key) - certificate: "com.android.appsearch", -} - -// Encapsulate the contributions made by the com.android.appsearch to the bootclasspath. -bootclasspath_fragment { - name: "com.android.appsearch-bootclasspath-fragment", - contents: ["framework-appsearch"], - apex_available: ["com.android.appsearch"], - - // The bootclasspath_fragments that provide APIs on which this depends. - fragments: [ - { - apex: "com.android.art", - module: "art-bootclasspath-fragment", - }, - ], - - // Additional stubs libraries that this fragment's contents use which are - // not provided by another bootclasspath_fragment. - additional_stubs: [ - "android-non-updatable", - ], -} - -// Encapsulate the contributions made by the com.android.appsearch to the systemserverclasspath. -systemserverclasspath_fragment { - name: "com.android.appsearch-systemserverclasspath-fragment", - contents: ["service-appsearch"], - apex_available: ["com.android.appsearch"], -} diff --git a/apex/appsearch/OWNERS b/apex/appsearch/OWNERS deleted file mode 100644 index 1703369271fad..0000000000000 --- a/apex/appsearch/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -adorokhine@google.com -sudheersai@google.com -yamasani@google.com diff --git a/apex/appsearch/apex_manifest.json b/apex/appsearch/apex_manifest.json deleted file mode 100644 index 39a2d38fa6426..0000000000000 --- a/apex/appsearch/apex_manifest.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "com.android.appsearch", - "version": 300000000 -} diff --git a/apex/appsearch/com.android.appsearch.avbpubkey b/apex/appsearch/com.android.appsearch.avbpubkey deleted file mode 100644 index 4e5acae9c1e4db9246388b7d0caf3210dfce212d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1032 zcmV+j1o!&@01yC)TTwl(k6->f=pxAa%L|d3 z_IY)*?dfF7xFB52r`ojfiL@Y-o$z7wn!L=7ckw&<*T1-)w3lX&Ywk)>q~ja%s8 zx}jL5j`J(SGZ}*oGg`Lwp<`fScXU9~7^PyBkQKjEznAzEOIexw<#pID$Gb55=08M; zrUY6N$^JllFcctG7@>>A&nrQi>b3pn4IaOl+SW|O-#oCDVa*)F_R^HecyGkzb}kQ7 zA>~t8Y!IphryXlZd+lj+4(iC;@N2{Oj~( zD5i7EtYOFEzqFb!eAg)keiMp%I~$9egn@b2A!=N1%%j|qr68e~ zEs9|UY)xhD<}>THs4LV@Sj_*hKO#L>y_N@=i4DN+*;s4c5FtubjHOGWBVbRGn!Q|8 z*_c9xE86v>@??G#>4rOfNb=&LNQUwD=>lyCjtQg0)heo0J9_Ir+T+P z)!aG7k?C;L^e?fPr3P2fS{e7-H(7gf?t7Z!&5gc$SP(l`j%N^j*T@UOP<5_ti^GfS=Kn0}Q)o)AZ=z91fqwE9QI zBLZ~;ag*AacWtlDu!bO(BP~4#am%m>?7uphhT+v_Pp{#pNI#8+uV9$I$3 z;3bPG_5nUF)(EGstL+65OPZ73&LLcU9Ww46I2p@XrGF@QKbIr6j1BHPMBBYzB3IJ; zrJmeolrekiPZ{(w7lTcqW9*#%F8>g1aA74B*Tfh=RjUNv5lv2`~NRkHyy(mM8L|-twM8$VUS`A*{ad?+Rm? z_m_X8MzLZ>%jhBt24eslXZCSWq3a-vvo$T|vk#O%;{E_Jr2+#1009Dm0stbK=$7>rUltX&`wz;} z6Cu(S0Thi8SEt&CYwN7*V`E)jqNcEff(Jlv7=N?vT6)zCd!Qo1$mXGpbZ=A%>j105 zT}IVSPLts?_Y6*D6Q}o`ANE0xk;RoJG!o)}s73oyG#O~kix#Y9y5uM9X2>&0NO?2O z13m+*^AR0VJj(!XNfG<98Fk6Pa6+7_R@(C=Fy|=V;%{DMgkqu;D7BR8YNw?ET=em6 zM7e^hCdt$|;%y7l&x#L*)y*j`jMCQcjGm6K|szZu~ zRBeAbqYkW|vSw%?%`Spec0m2~1-2O>FIEYi2y_W-JM4v0Jxy@-CUFSBK9MTbNxjs8 zUGe#YJ2!d3;{a%C`$t0HcLO9XKF2L`fWztg$gl3Pe+=LBvnO2eg;2^Di16S_TI_)# zrlh~G7b;`id>0GfW;A3*L4*3%E(FH#2iE3ebJxcZKca7+V1WXH0RaHuaZwZ%4li41 z+LH*WfUz`aI0^s4NMTn(+6r)rlKPxTH+e44dY=yVzgX60A)@FW} zZme&MEg^)eU&_I`!?GwjuDbJXxq2~0ZJgi}CF32hyH{8!f2czrgDhh~^Pt+-JzD6^ zZrEcQz+--6XoSiDZ;;nJ@kJ+B+s0W&Ej|dT_}Db23WCky6@4?FrLz?I zKl%v16x02VYQ zqNv&b2xV+Q_mpK$uqawg!Kogtd@jP=gA&)80?+;$S_@YUdbvw#kT5TBb*a_a*dj}C z`m%x1bOc-XYvHAs6*b~rgV9;T_Wc>XA3;6`9TX{{v=*QmJ0Hvj9Y~2#=|9c03Gs1n zWYVR&_1Vo|7tosv6A(4orCyx=@mS(FO0eW)o}FXOI)HBUG=W8*y(1(4=E7ujAORMN zwURW>MF;GAF2fAElawLav)GYrauW{;utfv+5W3`!km@vybGZ_Pzq<+=5xt>l0GP*d z##D*V%*f~;%}fyk6a+4$91(`X8{f&!q;V9(*uZN^E*`7mB|tQ}0)hbmBb@hdhf~Ah z?nYR2RA~-1F`!Dl;ddCa{_&U`d2o*cK&+z9EdYh+h4)sIVZ}iBEpc7iYqOcjgdge+ zhC9-7w3ErO!c+#?EPu!1C>;2oE#H!B1qeEiH=3Ag8hcQMwU1;3nwWOaog`{buRfL$ zK%c1yIJ0zC7P0&I4UeW=@UHy%95BpHSR7BTl0o4Vp^zhIMWWoC+p%b>@|^M+pp`dJ zNy5V$dKEq(WICgBljvUI%o4iIwDmRgDts~|BXJ;<-A0YCF`;>;*+uLDjqZ6eSM$>6 zMKgxE?>sCr0GK>^gk>^OKTp!$VKdn(1fC+NE#S6MdyTKYDmKYA0)hbmGvk4jeCZ~a z1SZ2Ri}6jnb>tqB3=~6BNWbj`dy3e5ZDHh;yzm1pby*7V#(j4Y z8tEt`i^S`p`lyr#`XclAahO4-ja!=}w*ohTQ1&F)X2x_R6)mp+LV|!aruRq!mYmfD z$Kv9thV!SsK%A0qW2=JQ&t%uRN$2MIqBvY3IWt-@CUJ5D%6qN=@I$XFlm ru`0BxN@j;~lNy0VI!h?y?ZssYmu>_Pb&hsuA$-LK+Ez!J9^S%`+ { - method @NonNull public java.util.Map> getAll(); - method @NonNull public java.util.Map> getFailures(); - method @NonNull public java.util.Map getSuccesses(); - method public boolean isSuccess(); - } - - public static final class AppSearchBatchResult.Builder { - ctor public AppSearchBatchResult.Builder(); - method @NonNull public android.app.appsearch.AppSearchBatchResult build(); - method @NonNull public android.app.appsearch.AppSearchBatchResult.Builder setFailure(@NonNull KeyType, int, @Nullable String); - method @NonNull public android.app.appsearch.AppSearchBatchResult.Builder setResult(@NonNull KeyType, @NonNull android.app.appsearch.AppSearchResult); - method @NonNull public android.app.appsearch.AppSearchBatchResult.Builder setSuccess(@NonNull KeyType, @Nullable ValueType); - } - - public class AppSearchManager { - method public void createGlobalSearchSession(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer>); - method public void createSearchSession(@NonNull android.app.appsearch.AppSearchManager.SearchContext, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer>); - } - - public static final class AppSearchManager.SearchContext { - method @NonNull public String getDatabaseName(); - } - - public static final class AppSearchManager.SearchContext.Builder { - ctor public AppSearchManager.SearchContext.Builder(@NonNull String); - method @NonNull public android.app.appsearch.AppSearchManager.SearchContext build(); - } - - public final class AppSearchResult { - method @Nullable public String getErrorMessage(); - method public int getResultCode(); - method @Nullable public ValueType getResultValue(); - method public boolean isSuccess(); - method @NonNull public static android.app.appsearch.AppSearchResult newFailedResult(int, @Nullable String); - method @NonNull public static android.app.appsearch.AppSearchResult newSuccessfulResult(@Nullable ValueType); - field public static final int RESULT_INTERNAL_ERROR = 2; // 0x2 - field public static final int RESULT_INVALID_ARGUMENT = 3; // 0x3 - field public static final int RESULT_INVALID_SCHEMA = 7; // 0x7 - field public static final int RESULT_IO_ERROR = 4; // 0x4 - field public static final int RESULT_NOT_FOUND = 6; // 0x6 - field public static final int RESULT_OK = 0; // 0x0 - field public static final int RESULT_OUT_OF_SPACE = 5; // 0x5 - field public static final int RESULT_SECURITY_ERROR = 8; // 0x8 - field public static final int RESULT_UNKNOWN_ERROR = 1; // 0x1 - } - - public final class AppSearchSchema { - method @NonNull public java.util.List getProperties(); - method @NonNull public String getSchemaType(); - } - - public static final class AppSearchSchema.BooleanPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig { - } - - public static final class AppSearchSchema.BooleanPropertyConfig.Builder { - ctor public AppSearchSchema.BooleanPropertyConfig.Builder(@NonNull String); - method @NonNull public android.app.appsearch.AppSearchSchema.BooleanPropertyConfig build(); - method @NonNull public android.app.appsearch.AppSearchSchema.BooleanPropertyConfig.Builder setCardinality(int); - } - - public static final class AppSearchSchema.Builder { - ctor public AppSearchSchema.Builder(@NonNull String); - method @NonNull public android.app.appsearch.AppSearchSchema.Builder addProperty(@NonNull android.app.appsearch.AppSearchSchema.PropertyConfig); - method @NonNull public android.app.appsearch.AppSearchSchema build(); - } - - public static final class AppSearchSchema.BytesPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig { - } - - public static final class AppSearchSchema.BytesPropertyConfig.Builder { - ctor public AppSearchSchema.BytesPropertyConfig.Builder(@NonNull String); - method @NonNull public android.app.appsearch.AppSearchSchema.BytesPropertyConfig build(); - method @NonNull public android.app.appsearch.AppSearchSchema.BytesPropertyConfig.Builder setCardinality(int); - } - - public static final class AppSearchSchema.DocumentPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig { - method @NonNull public String getSchemaType(); - method public boolean shouldIndexNestedProperties(); - } - - public static final class AppSearchSchema.DocumentPropertyConfig.Builder { - ctor public AppSearchSchema.DocumentPropertyConfig.Builder(@NonNull String, @NonNull String); - method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig build(); - method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setCardinality(int); - method @NonNull public android.app.appsearch.AppSearchSchema.DocumentPropertyConfig.Builder setShouldIndexNestedProperties(boolean); - } - - public static final class AppSearchSchema.DoublePropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig { - } - - public static final class AppSearchSchema.DoublePropertyConfig.Builder { - ctor public AppSearchSchema.DoublePropertyConfig.Builder(@NonNull String); - method @NonNull public android.app.appsearch.AppSearchSchema.DoublePropertyConfig build(); - method @NonNull public android.app.appsearch.AppSearchSchema.DoublePropertyConfig.Builder setCardinality(int); - } - - public static final class AppSearchSchema.LongPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig { - } - - public static final class AppSearchSchema.LongPropertyConfig.Builder { - ctor public AppSearchSchema.LongPropertyConfig.Builder(@NonNull String); - method @NonNull public android.app.appsearch.AppSearchSchema.LongPropertyConfig build(); - method @NonNull public android.app.appsearch.AppSearchSchema.LongPropertyConfig.Builder setCardinality(int); - } - - public abstract static class AppSearchSchema.PropertyConfig { - method public int getCardinality(); - method @NonNull public String getName(); - field public static final int CARDINALITY_OPTIONAL = 2; // 0x2 - field public static final int CARDINALITY_REPEATED = 1; // 0x1 - field public static final int CARDINALITY_REQUIRED = 3; // 0x3 - } - - public static final class AppSearchSchema.StringPropertyConfig extends android.app.appsearch.AppSearchSchema.PropertyConfig { - method public int getIndexingType(); - method public int getTokenizerType(); - field public static final int INDEXING_TYPE_EXACT_TERMS = 1; // 0x1 - field public static final int INDEXING_TYPE_NONE = 0; // 0x0 - field public static final int INDEXING_TYPE_PREFIXES = 2; // 0x2 - field public static final int TOKENIZER_TYPE_NONE = 0; // 0x0 - field public static final int TOKENIZER_TYPE_PLAIN = 1; // 0x1 - } - - public static final class AppSearchSchema.StringPropertyConfig.Builder { - ctor public AppSearchSchema.StringPropertyConfig.Builder(@NonNull String); - method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig build(); - method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder setCardinality(int); - method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder setIndexingType(int); - method @NonNull public android.app.appsearch.AppSearchSchema.StringPropertyConfig.Builder setTokenizerType(int); - } - - public final class AppSearchSession implements java.io.Closeable { - method public void close(); - method public void getByDocumentId(@NonNull android.app.appsearch.GetByDocumentIdRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback); - method public void getNamespaces(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer>>); - method public void getSchema(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer>); - method public void getStorageInfo(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer>); - method public void put(@NonNull android.app.appsearch.PutDocumentsRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback); - method public void remove(@NonNull android.app.appsearch.RemoveByDocumentIdRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback); - method public void remove(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer>); - method public void reportUsage(@NonNull android.app.appsearch.ReportUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer>); - method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec); - method public void setSchema(@NonNull android.app.appsearch.SetSchemaRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer>); - } - - public interface BatchResultCallback { - method public void onResult(@NonNull android.app.appsearch.AppSearchBatchResult); - method public default void onSystemError(@Nullable Throwable); - } - - public class GenericDocument { - ctor protected GenericDocument(@NonNull android.app.appsearch.GenericDocument); - method public long getCreationTimestampMillis(); - method @NonNull public String getId(); - method public static int getMaxIndexedProperties(); - method @NonNull public String getNamespace(); - method @Nullable public Object getProperty(@NonNull String); - method public boolean getPropertyBoolean(@NonNull String); - method @Nullable public boolean[] getPropertyBooleanArray(@NonNull String); - method @Nullable public byte[] getPropertyBytes(@NonNull String); - method @Nullable public byte[][] getPropertyBytesArray(@NonNull String); - method @Nullable public android.app.appsearch.GenericDocument getPropertyDocument(@NonNull String); - method @Nullable public android.app.appsearch.GenericDocument[] getPropertyDocumentArray(@NonNull String); - method public double getPropertyDouble(@NonNull String); - method @Nullable public double[] getPropertyDoubleArray(@NonNull String); - method public long getPropertyLong(@NonNull String); - method @Nullable public long[] getPropertyLongArray(@NonNull String); - method @NonNull public java.util.Set getPropertyNames(); - method @Nullable public String getPropertyString(@NonNull String); - method @Nullable public String[] getPropertyStringArray(@NonNull String); - method @NonNull public String getSchemaType(); - method public int getScore(); - method public long getTtlMillis(); - } - - public static class GenericDocument.Builder { - ctor public GenericDocument.Builder(@NonNull String, @NonNull String, @NonNull String); - method @NonNull public android.app.appsearch.GenericDocument build(); - method @NonNull public BuilderType setCreationTimestampMillis(long); - method @NonNull public BuilderType setPropertyBoolean(@NonNull String, @NonNull boolean...); - method @NonNull public BuilderType setPropertyBytes(@NonNull String, @NonNull byte[]...); - method @NonNull public BuilderType setPropertyDocument(@NonNull String, @NonNull android.app.appsearch.GenericDocument...); - method @NonNull public BuilderType setPropertyDouble(@NonNull String, @NonNull double...); - method @NonNull public BuilderType setPropertyLong(@NonNull String, @NonNull long...); - method @NonNull public BuilderType setPropertyString(@NonNull String, @NonNull java.lang.String...); - method @NonNull public BuilderType setScore(@IntRange(from=0, to=java.lang.Integer.MAX_VALUE) int); - method @NonNull public BuilderType setTtlMillis(long); - } - - public final class GetByDocumentIdRequest { - method @NonNull public java.util.Set getIds(); - method @NonNull public String getNamespace(); - method @NonNull public java.util.Map> getProjections(); - field public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*"; - } - - public static final class GetByDocumentIdRequest.Builder { - ctor public GetByDocumentIdRequest.Builder(@NonNull String); - method @NonNull public android.app.appsearch.GetByDocumentIdRequest.Builder addIds(@NonNull java.lang.String...); - method @NonNull public android.app.appsearch.GetByDocumentIdRequest.Builder addIds(@NonNull java.util.Collection); - method @NonNull public android.app.appsearch.GetByDocumentIdRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection); - method @NonNull public android.app.appsearch.GetByDocumentIdRequest build(); - } - - public final class GetSchemaResponse { - method @NonNull public java.util.Set getSchemas(); - method @IntRange(from=0) public int getVersion(); - } - - public static final class GetSchemaResponse.Builder { - ctor public GetSchemaResponse.Builder(); - method @NonNull public android.app.appsearch.GetSchemaResponse.Builder addSchema(@NonNull android.app.appsearch.AppSearchSchema); - method @NonNull public android.app.appsearch.GetSchemaResponse build(); - method @NonNull public android.app.appsearch.GetSchemaResponse.Builder setVersion(@IntRange(from=0) int); - } - - public class GlobalSearchSession implements java.io.Closeable { - method public void close(); - method public void reportSystemUsage(@NonNull android.app.appsearch.ReportSystemUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer>); - method @NonNull public android.app.appsearch.SearchResults search(@NonNull String, @NonNull android.app.appsearch.SearchSpec); - } - - public abstract class Migrator { - ctor public Migrator(); - method @NonNull @WorkerThread public abstract android.app.appsearch.GenericDocument onDowngrade(int, int, @NonNull android.app.appsearch.GenericDocument); - method @NonNull @WorkerThread public abstract android.app.appsearch.GenericDocument onUpgrade(int, int, @NonNull android.app.appsearch.GenericDocument); - method public abstract boolean shouldMigrate(int, int); - } - - public class PackageIdentifier { - ctor public PackageIdentifier(@NonNull String, @NonNull byte[]); - method @NonNull public String getPackageName(); - method @NonNull public byte[] getSha256Certificate(); - } - - public final class PutDocumentsRequest { - method @NonNull public java.util.List getGenericDocuments(); - } - - public static final class PutDocumentsRequest.Builder { - ctor public PutDocumentsRequest.Builder(); - method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocuments(@NonNull android.app.appsearch.GenericDocument...); - method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocuments(@NonNull java.util.Collection); - method @NonNull public android.app.appsearch.PutDocumentsRequest build(); - } - - public final class RemoveByDocumentIdRequest { - method @NonNull public java.util.Set getIds(); - method @NonNull public String getNamespace(); - } - - public static final class RemoveByDocumentIdRequest.Builder { - ctor public RemoveByDocumentIdRequest.Builder(@NonNull String); - method @NonNull public android.app.appsearch.RemoveByDocumentIdRequest.Builder addIds(@NonNull java.lang.String...); - method @NonNull public android.app.appsearch.RemoveByDocumentIdRequest.Builder addIds(@NonNull java.util.Collection); - method @NonNull public android.app.appsearch.RemoveByDocumentIdRequest build(); - } - - public final class ReportSystemUsageRequest { - method @NonNull public String getDatabaseName(); - method @NonNull public String getDocumentId(); - method @NonNull public String getNamespace(); - method @NonNull public String getPackageName(); - method public long getUsageTimestampMillis(); - } - - public static final class ReportSystemUsageRequest.Builder { - ctor public ReportSystemUsageRequest.Builder(@NonNull String, @NonNull String, @NonNull String, @NonNull String); - method @NonNull public android.app.appsearch.ReportSystemUsageRequest build(); - method @NonNull public android.app.appsearch.ReportSystemUsageRequest.Builder setUsageTimestampMillis(long); - } - - public final class ReportUsageRequest { - method @NonNull public String getDocumentId(); - method @NonNull public String getNamespace(); - method public long getUsageTimestampMillis(); - } - - public static final class ReportUsageRequest.Builder { - ctor public ReportUsageRequest.Builder(@NonNull String, @NonNull String); - method @NonNull public android.app.appsearch.ReportUsageRequest build(); - method @NonNull public android.app.appsearch.ReportUsageRequest.Builder setUsageTimestampMillis(long); - } - - public final class SearchResult { - method @NonNull public String getDatabaseName(); - method @NonNull public android.app.appsearch.GenericDocument getGenericDocument(); - method @NonNull public java.util.List getMatchInfos(); - method @NonNull public String getPackageName(); - method public double getRankingSignal(); - } - - public static final class SearchResult.Builder { - ctor public SearchResult.Builder(@NonNull String, @NonNull String); - method @NonNull public android.app.appsearch.SearchResult.Builder addMatchInfo(@NonNull android.app.appsearch.SearchResult.MatchInfo); - method @NonNull public android.app.appsearch.SearchResult build(); - method @NonNull public android.app.appsearch.SearchResult.Builder setGenericDocument(@NonNull android.app.appsearch.GenericDocument); - method @NonNull public android.app.appsearch.SearchResult.Builder setRankingSignal(double); - } - - public static final class SearchResult.MatchInfo { - method @NonNull public CharSequence getExactMatch(); - method @NonNull public android.app.appsearch.SearchResult.MatchRange getExactMatchRange(); - method @NonNull public String getFullText(); - method @NonNull public String getPropertyPath(); - method @NonNull public CharSequence getSnippet(); - method @NonNull public android.app.appsearch.SearchResult.MatchRange getSnippetRange(); - } - - public static final class SearchResult.MatchInfo.Builder { - ctor public SearchResult.MatchInfo.Builder(@NonNull String); - method @NonNull public android.app.appsearch.SearchResult.MatchInfo build(); - method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setExactMatchRange(@NonNull android.app.appsearch.SearchResult.MatchRange); - method @NonNull public android.app.appsearch.SearchResult.MatchInfo.Builder setSnippetRange(@NonNull android.app.appsearch.SearchResult.MatchRange); - } - - public static final class SearchResult.MatchRange { - ctor public SearchResult.MatchRange(int, int); - method public int getEnd(); - method public int getStart(); - } - - public class SearchResults implements java.io.Closeable { - method public void close(); - method public void getNextPage(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer>>); - } - - public final class SearchSpec { - method @NonNull public java.util.List getFilterNamespaces(); - method @NonNull public java.util.List getFilterPackageNames(); - method @NonNull public java.util.List getFilterSchemas(); - method public int getMaxSnippetSize(); - method public int getOrder(); - method @NonNull public java.util.Map> getProjections(); - method public int getRankingStrategy(); - method public int getResultCountPerPage(); - method public int getResultGroupingLimit(); - method public int getResultGroupingTypeFlags(); - method public int getSnippetCount(); - method public int getSnippetCountPerProperty(); - method public int getTermMatch(); - field public static final int GROUPING_TYPE_PER_NAMESPACE = 2; // 0x2 - field public static final int GROUPING_TYPE_PER_PACKAGE = 1; // 0x1 - field public static final int ORDER_ASCENDING = 1; // 0x1 - field public static final int ORDER_DESCENDING = 0; // 0x0 - field public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*"; - field public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2; // 0x2 - field public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1; // 0x1 - field public static final int RANKING_STRATEGY_NONE = 0; // 0x0 - field public static final int RANKING_STRATEGY_RELEVANCE_SCORE = 3; // 0x3 - field public static final int RANKING_STRATEGY_SYSTEM_USAGE_COUNT = 6; // 0x6 - field public static final int RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP = 7; // 0x7 - field public static final int RANKING_STRATEGY_USAGE_COUNT = 4; // 0x4 - field public static final int RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP = 5; // 0x5 - field public static final int TERM_MATCH_EXACT_ONLY = 1; // 0x1 - field public static final int TERM_MATCH_PREFIX = 2; // 0x2 - } - - public static final class SearchSpec.Builder { - ctor public SearchSpec.Builder(); - method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterNamespaces(@NonNull java.lang.String...); - method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterNamespaces(@NonNull java.util.Collection); - method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.lang.String...); - method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.util.Collection); - method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.lang.String...); - method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterSchemas(@NonNull java.util.Collection); - method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.util.Collection); - method @NonNull public android.app.appsearch.SearchSpec build(); - method @NonNull public android.app.appsearch.SearchSpec.Builder setMaxSnippetSize(@IntRange(from=0, to=0x2710) int); - method @NonNull public android.app.appsearch.SearchSpec.Builder setOrder(int); - method @NonNull public android.app.appsearch.SearchSpec.Builder setRankingStrategy(int); - method @NonNull public android.app.appsearch.SearchSpec.Builder setResultCountPerPage(@IntRange(from=0, to=0x2710) int); - method @NonNull public android.app.appsearch.SearchSpec.Builder setResultGrouping(int, int); - method @NonNull public android.app.appsearch.SearchSpec.Builder setSnippetCount(@IntRange(from=0, to=0x2710) int); - method @NonNull public android.app.appsearch.SearchSpec.Builder setSnippetCountPerProperty(@IntRange(from=0, to=0x2710) int); - method @NonNull public android.app.appsearch.SearchSpec.Builder setTermMatch(int); - } - - public final class SetSchemaRequest { - method @NonNull public java.util.Map getMigrators(); - method @NonNull public java.util.Set getSchemas(); - method @NonNull public java.util.Set getSchemasNotDisplayedBySystem(); - method @NonNull public java.util.Map> getSchemasVisibleToPackages(); - method @IntRange(from=1) public int getVersion(); - method public boolean isForceOverride(); - } - - public static final class SetSchemaRequest.Builder { - ctor public SetSchemaRequest.Builder(); - method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchemas(@NonNull android.app.appsearch.AppSearchSchema...); - method @NonNull public android.app.appsearch.SetSchemaRequest.Builder addSchemas(@NonNull java.util.Collection); - method @NonNull public android.app.appsearch.SetSchemaRequest build(); - method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean); - method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrator(@NonNull String, @NonNull android.app.appsearch.Migrator); - method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrators(@NonNull java.util.Map); - method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeDisplayedBySystem(@NonNull String, boolean); - method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(@NonNull String, boolean, @NonNull android.app.appsearch.PackageIdentifier); - method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setVersion(@IntRange(from=1) int); - } - - public class SetSchemaResponse { - method @NonNull public java.util.Set getDeletedTypes(); - method @NonNull public java.util.Set getIncompatibleTypes(); - method @NonNull public java.util.Set getMigratedTypes(); - method @NonNull public java.util.List getMigrationFailures(); - } - - public static final class SetSchemaResponse.Builder { - ctor public SetSchemaResponse.Builder(); - method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addDeletedType(@NonNull String); - method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addDeletedTypes(@NonNull java.util.Collection); - method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleType(@NonNull String); - method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleTypes(@NonNull java.util.Collection); - method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedType(@NonNull String); - method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedTypes(@NonNull java.util.Collection); - method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigrationFailure(@NonNull android.app.appsearch.SetSchemaResponse.MigrationFailure); - method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigrationFailures(@NonNull java.util.Collection); - method @NonNull public android.app.appsearch.SetSchemaResponse build(); - } - - public static class SetSchemaResponse.MigrationFailure { - ctor public SetSchemaResponse.MigrationFailure(@NonNull String, @NonNull String, @NonNull String, @NonNull android.app.appsearch.AppSearchResult); - method @NonNull public android.app.appsearch.AppSearchResult getAppSearchResult(); - method @NonNull public String getDocumentId(); - method @NonNull public String getNamespace(); - method @NonNull public String getSchemaType(); - } - - public class StorageInfo { - method public int getAliveDocumentsCount(); - method public int getAliveNamespacesCount(); - method public long getSizeBytes(); - } - - public static final class StorageInfo.Builder { - ctor public StorageInfo.Builder(); - method @NonNull public android.app.appsearch.StorageInfo build(); - method @NonNull public android.app.appsearch.StorageInfo.Builder setAliveDocumentsCount(int); - method @NonNull public android.app.appsearch.StorageInfo.Builder setAliveNamespacesCount(int); - method @NonNull public android.app.appsearch.StorageInfo.Builder setSizeBytes(long); - } - -} - -package android.app.appsearch.exceptions { - - public class AppSearchException extends java.lang.Exception { - ctor public AppSearchException(int); - ctor public AppSearchException(int, @Nullable String); - ctor public AppSearchException(int, @Nullable String, @Nullable Throwable); - method public int getResultCode(); - method @NonNull public android.app.appsearch.AppSearchResult toAppSearchResult(); - } - -} - diff --git a/apex/appsearch/framework/api/module-lib-current.txt b/apex/appsearch/framework/api/module-lib-current.txt deleted file mode 100644 index d802177e249b3..0000000000000 --- a/apex/appsearch/framework/api/module-lib-current.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/appsearch/framework/api/module-lib-removed.txt b/apex/appsearch/framework/api/module-lib-removed.txt deleted file mode 100644 index d802177e249b3..0000000000000 --- a/apex/appsearch/framework/api/module-lib-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/appsearch/framework/api/removed.txt b/apex/appsearch/framework/api/removed.txt deleted file mode 100644 index d802177e249b3..0000000000000 --- a/apex/appsearch/framework/api/removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/appsearch/framework/api/system-current.txt b/apex/appsearch/framework/api/system-current.txt deleted file mode 100644 index 4a6194e2915db..0000000000000 --- a/apex/appsearch/framework/api/system-current.txt +++ /dev/null @@ -1,9 +0,0 @@ -// Signature format: 2.0 -package android.app.appsearch { - - public class AppSearchManagerFrameworkInitializer { - method public static void initialize(); - } - -} - diff --git a/apex/appsearch/framework/api/system-removed.txt b/apex/appsearch/framework/api/system-removed.txt deleted file mode 100644 index d802177e249b3..0000000000000 --- a/apex/appsearch/framework/api/system-removed.txt +++ /dev/null @@ -1 +0,0 @@ -// Signature format: 2.0 diff --git a/apex/appsearch/framework/jarjar-rules.txt b/apex/appsearch/framework/jarjar-rules.txt deleted file mode 100644 index 50c3ee4125a8a..0000000000000 --- a/apex/appsearch/framework/jarjar-rules.txt +++ /dev/null @@ -1,6 +0,0 @@ -# Rename all com.android.internal.util classes to prevent class name collisions -# between this module and the other versions of the utility classes linked into -# the framework. - -# These must be kept in sync with the sources of framework-utils-appsearch -rule com.android.internal.util.Preconditions* android.app.appsearch.internal.util.Preconditions@1 diff --git a/apex/appsearch/framework/java/TEST_MAPPING b/apex/appsearch/framework/java/TEST_MAPPING deleted file mode 100644 index 12188f83a29f6..0000000000000 --- a/apex/appsearch/framework/java/TEST_MAPPING +++ /dev/null @@ -1,7 +0,0 @@ -{ - "imports": [ - { - "path": "frameworks/base/apex/appsearch/service/java/com/android/server/appsearch" - } - ] -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java deleted file mode 100644 index fb1cccf54f2b5..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 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 android.annotation.CallbackExecutor; -import android.annotation.NonNull; -import android.annotation.SystemService; -import android.annotation.UserHandleAware; -import android.app.appsearch.aidl.IAppSearchManager; -import android.content.Context; - -import com.android.internal.util.Preconditions; - -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -/** - * Provides access to the centralized AppSearch index maintained by the system. - * - *

AppSearch is an offline, on-device search library for managing structured data featuring: - * - *

    - *
  • APIs to index and retrieve data via full-text search. - *
  • An API for applications to explicitly grant read-access permission of their data to other - * applications. - * See: {@link SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} - *
  • An API for applications to opt into or out of having their data displayed on System UI - * surfaces by the System-designated global querier. - * See: {@link SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} - *
- * - *

Applications create a database by opening an {@link AppSearchSession}. - * - *

Example: - * - *

- * AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
- *
- * AppSearchManager.SearchContext searchContext = new AppSearchManager.SearchContext.Builder().
- *    setDatabaseName(dbName).build());
- * appSearchManager.createSearchSession(searchContext, mExecutor, appSearchSessionResult -> {
- *      mAppSearchSession = appSearchSessionResult.getResultValue();
- * });
- * - *

After opening the session, a schema must be set in order to define the organizational - * structure of data. The schema is set by calling {@link AppSearchSession#setSchema}. The schema is - * composed of a collection of {@link AppSearchSchema} objects, each of which defines a unique type - * of data. - * - *

Example: - * - *

- * AppSearchSchema emailSchemaType = new AppSearchSchema.Builder("Email")
- *     .addProperty(new StringPropertyConfig.Builder("subject")
- *        .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
- *        .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES)
- *        .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN)
- *    .build()
- * ).build();
- *
- * SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(emailSchemaType).build();
- * mAppSearchSession.set(request, mExecutor, appSearchResult -> {
- *      if (appSearchResult.isSuccess()) {
- *           //Schema has been successfully set.
- *      }
- * });
- * - *

The basic unit of data in AppSearch is represented as a {@link GenericDocument} object, - * containing an ID, namespace, time-to-live, score, and properties. A namespace organizes a logical - * group of documents. For example, a namespace can be created to group documents on a per-account - * basis. An ID identifies a single document within a namespace. The combination of namespace and ID - * uniquely identifies a {@link GenericDocument} in the database. - * - *

Once the schema has been set, {@link GenericDocument} objects can be put into the database and - * indexed by calling {@link AppSearchSession#put}. - * - *

Example: - * - *

- * // Although for this example we use GenericDocument directly, we recommend extending
- * // GenericDocument to create specific types (i.e. Email) with specific setters/getters.
- * GenericDocument email = new GenericDocument.Builder<>(NAMESPACE, ID, EMAIL_SCHEMA_TYPE)
- *     .setPropertyString(“subject”, EMAIL_SUBJECT)
- *     .setScore(EMAIL_SCORE)
- *     .build();
- *
- * PutDocumentsRequest request = new PutDocumentsRequest.Builder().addGenericDocuments(email)
- *     .build();
- * mAppSearchSession.put(request, mExecutor, appSearchBatchResult -> {
- *      if (appSearchBatchResult.isSuccess()) {
- *           //All documents have been successfully indexed.
- *      }
- * });
- * - *

Searching within the database is done by calling {@link AppSearchSession#search} and providing - * the query string to search for, as well as a {@link SearchSpec}. - * - *

Alternatively, {@link AppSearchSession#getByDocumentId} can be called to retrieve documents by - * namespace and ID. - * - *

Document removal is done either by time-to-live expiration, or explicitly calling a remove - * operation. Remove operations can be done by namespace and ID via {@link - * AppSearchSession#remove(RemoveByDocumentIdRequest, Executor, BatchResultCallback)}, or by query - * via {@link AppSearchSession#remove(String, SearchSpec, Executor, Consumer)}. - */ -@SystemService(Context.APP_SEARCH_SERVICE) -public class AppSearchManager { - - private final IAppSearchManager mService; - private final Context mContext; - - /** @hide */ - public AppSearchManager(@NonNull Context context, @NonNull IAppSearchManager service) { - mContext = Objects.requireNonNull(context); - mService = Objects.requireNonNull(service); - } - - /** Contains information about how to create the search session. */ - public static final class SearchContext { - final String mDatabaseName; - - SearchContext(@NonNull String databaseName) { - mDatabaseName = Objects.requireNonNull(databaseName); - } - - /** - * Returns the name of the database to create or open. - * - *

Databases with different names are fully separate with distinct types, namespaces, and - * data. - */ - @NonNull - public String getDatabaseName() { - return mDatabaseName; - } - - /** Builder for {@link SearchContext} objects. */ - public static final class Builder { - private final String mDatabaseName; - private boolean mBuilt = false; - - /** - * Creates a new {@link SearchContext.Builder}. - * - *

{@link AppSearchSession} will create or open a database under the given name. - * - *

Databases with different names are fully separate with distinct types, namespaces, - * and data. - * - *

Database name cannot contain {@code '/'}. - * - * @param databaseName The name of the database. - * @throws IllegalArgumentException if the databaseName contains {@code '/'}. - */ - public Builder(@NonNull String databaseName) { - Objects.requireNonNull(databaseName); - Preconditions.checkArgument( - !databaseName.contains("/"), "Database name cannot contain '/'"); - mDatabaseName = databaseName; - } - - /** Builds a {@link SearchContext} instance. */ - @NonNull - public SearchContext build() { - Preconditions.checkState(!mBuilt, "Builder has already been used"); - mBuilt = true; - return new SearchContext(mDatabaseName); - } - } - } - - /** - * Creates a new {@link AppSearchSession}. - * - *

This process requires an AppSearch native indexing file system. If it's not created, the - * initialization process will create one under the user's credential encrypted directory. - * - * @param searchContext The {@link SearchContext} contains all information to create a new - * {@link AppSearchSession} - * @param executor Executor on which to invoke the callback. - * @param callback The {@link AppSearchResult}<{@link AppSearchSession}> of performing - * this operation. Or a {@link AppSearchResult} with failure reason code and error - * information. - */ - @UserHandleAware - public void createSearchSession( - @NonNull SearchContext searchContext, - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer> callback) { - Objects.requireNonNull(searchContext); - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - AppSearchSession.createSearchSession( - searchContext, - mService, - mContext.getUser(), - getPackageName(), - executor, - callback); - } - - /** - * Creates a new {@link GlobalSearchSession}. - * - *

This process requires an AppSearch native indexing file system. If it's not created, the - * initialization process will create one under the user's credential encrypted directory. - * - * @param executor Executor on which to invoke the callback. - * @param callback The {@link AppSearchResult}<{@link GlobalSearchSession}> of performing - * this operation. Or a {@link AppSearchResult} with failure reason code and error - * information. - */ - @UserHandleAware - public void createGlobalSearchSession( - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer> callback) { - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - GlobalSearchSession.createGlobalSearchSession( - mService, mContext.getUser(), getPackageName(), executor, callback); - } - - /** Returns the package name that should be used for uid verification. */ - @NonNull - private String getPackageName() { - return mContext.getOpPackageName(); - } -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java deleted file mode 100644 index 7dc527aa28fbb..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManagerFrameworkInitializer.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.app.appsearch; - -import android.annotation.SystemApi; -import android.app.SystemServiceRegistry; -import android.app.appsearch.aidl.IAppSearchManager; -import android.content.Context; - -/** - * Class holding initialization code for the AppSearch module. - * - * @hide - */ -@SystemApi -public class AppSearchManagerFrameworkInitializer { - private AppSearchManagerFrameworkInitializer() {} - - /** - * Called by {@link SystemServiceRegistry}'s static initializer and registers all AppSearch - * services to {@link Context}, so that {@link Context#getSystemService} can return them. - * - * @throws IllegalStateException if this is called from anywhere besides - * {@link SystemServiceRegistry} - */ - public static void initialize() { - SystemServiceRegistry.registerContextAwareService( - Context.APP_SEARCH_SERVICE, AppSearchManager.class, - (context, service) -> - new AppSearchManager(context, IAppSearchManager.Stub.asInterface(service))); - } -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java deleted file mode 100644 index c7385047a6eaa..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchMigrationHelper.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * 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 android.app.appsearch; - -import static android.app.appsearch.AppSearchResult.RESULT_INVALID_SCHEMA; -import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; -import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY; - -import android.annotation.NonNull; -import android.annotation.WorkerThread; -import android.app.appsearch.aidl.AppSearchResultParcel; -import android.app.appsearch.aidl.IAppSearchManager; -import android.app.appsearch.aidl.IAppSearchResultCallback; -import android.app.appsearch.exceptions.AppSearchException; -import android.os.Bundle; -import android.os.Parcel; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.os.UserHandle; -import android.util.ArraySet; - -import java.io.Closeable; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -/** - * The helper class for {@link AppSearchSchema} migration. - * - *

It will query and migrate {@link GenericDocument} in given type to a new version. - * @hide - */ -public class AppSearchMigrationHelper implements Closeable { - private final IAppSearchManager mService; - private final String mPackageName; - private final String mDatabaseName; - private final UserHandle mUserHandle; - private final File mMigratedFile; - private final Set mDestinationTypes; - private boolean mAreDocumentsMigrated = false; - - AppSearchMigrationHelper(@NonNull IAppSearchManager service, - @NonNull UserHandle userHandle, - @NonNull String packageName, - @NonNull String databaseName, - @NonNull Set newSchemas) throws IOException { - mService = Objects.requireNonNull(service); - mUserHandle = Objects.requireNonNull(userHandle); - mPackageName = Objects.requireNonNull(packageName); - mDatabaseName = Objects.requireNonNull(databaseName); - mMigratedFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null); - mDestinationTypes = new ArraySet<>(newSchemas.size()); - for (AppSearchSchema newSchema : newSchemas) { - mDestinationTypes.add(newSchema.getSchemaType()); - } - } - - /** - * Queries all documents that need to be migrated to a different version and transform - * documents to that version by passing them to the provided {@link Migrator}. - * - *

The method will be executed on the executor provided to - * {@link AppSearchSession#setSchema}. - * - * @param schemaType The schema type that needs to be updated and whose {@link GenericDocument} - * need to be migrated. - * @param migrator The {@link Migrator} that will upgrade or downgrade a {@link - * GenericDocument} to new version. - */ - @WorkerThread - public void queryAndTransform(@NonNull String schemaType, @NonNull Migrator migrator, - int currentVersion, int finalVersion) - throws IOException, AppSearchException, InterruptedException, ExecutionException { - File queryFile = File.createTempFile(/*prefix=*/"appsearch", /*suffix=*/null); - try (ParcelFileDescriptor fileDescriptor = - ParcelFileDescriptor.open(queryFile, MODE_WRITE_ONLY)) { - CompletableFuture> future = new CompletableFuture<>(); - mService.writeQueryResultsToFile(mPackageName, mDatabaseName, - fileDescriptor, - /*queryExpression=*/ "", - new SearchSpec.Builder() - .addFilterSchemas(schemaType) - .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) - .build().getBundle(), - mUserHandle, - new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - future.complete(resultParcel.getResult()); - } - }); - AppSearchResult result = future.get(); - if (!result.isSuccess()) { - throw new AppSearchException(result.getResultCode(), result.getErrorMessage()); - } - readAndTransform(queryFile, migrator, currentVersion, finalVersion); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } finally { - queryFile.delete(); - } - } - - /** - * Puts all {@link GenericDocument} migrated from the previous call to - * {@link #queryAndTransform} into AppSearch. - * - *

This method should be only called once. - * - * @param responseBuilder a SetSchemaResponse builder whose result will be returned by this - * function with any - * {@link android.app.appsearch.SetSchemaResponse.MigrationFailure} - * added in. - * @return the {@link SetSchemaResponse} for {@link AppSearchSession#setSchema} call. - */ - @NonNull - AppSearchResult putMigratedDocuments( - @NonNull SetSchemaResponse.Builder responseBuilder) { - if (!mAreDocumentsMigrated) { - return AppSearchResult.newSuccessfulResult(responseBuilder.build()); - } - try (ParcelFileDescriptor fileDescriptor = - ParcelFileDescriptor.open(mMigratedFile, MODE_READ_ONLY)) { - CompletableFuture>> future = new CompletableFuture<>(); - mService.putDocumentsFromFile(mPackageName, mDatabaseName, fileDescriptor, mUserHandle, - new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - future.complete(resultParcel.getResult()); - } - }); - AppSearchResult> result = future.get(); - if (!result.isSuccess()) { - return AppSearchResult.newFailedResult(result); - } - List migratedFailureBundles = result.getResultValue(); - for (int i = 0; i < migratedFailureBundles.size(); i++) { - responseBuilder.addMigrationFailure( - new SetSchemaResponse.MigrationFailure(migratedFailureBundles.get(i))); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (Throwable t) { - return AppSearchResult.throwableToFailedResult(t); - } finally { - mMigratedFile.delete(); - } - return AppSearchResult.newSuccessfulResult(responseBuilder.build()); - } - - /** - * Reads all saved {@link GenericDocument}s from the given {@link File}. - * - *

Transforms those {@link GenericDocument}s to the final version. - * - *

Save migrated {@link GenericDocument}s to the {@link #mMigratedFile}. - */ - private void readAndTransform(@NonNull File file, @NonNull Migrator migrator, - int currentVersion, int finalVersion) - throws IOException, AppSearchException { - try (DataInputStream inputStream = new DataInputStream(new FileInputStream(file)); - DataOutputStream outputStream = new DataOutputStream(new FileOutputStream( - mMigratedFile, /*append=*/ true))) { - GenericDocument document; - while (true) { - try { - document = readDocumentFromInputStream(inputStream); - } catch (EOFException e) { - break; - // Nothing wrong. We just finished reading. - } - - GenericDocument newDocument; - if (currentVersion < finalVersion) { - newDocument = migrator.onUpgrade(currentVersion, finalVersion, document); - } else { - // currentVersion == finalVersion case won't trigger migration and get here. - newDocument = migrator.onDowngrade(currentVersion, finalVersion, document); - } - - if (!mDestinationTypes.contains(newDocument.getSchemaType())) { - // we exit before the new schema has been set to AppSearch. So no - // observable changes will be applied to stored schemas and documents. - // And the temp file will be deleted at close(), which will be triggered at - // the end of try-with-resources block of SearchSessionImpl. - throw new AppSearchException( - RESULT_INVALID_SCHEMA, - "Receive a migrated document with schema type: " - + newDocument.getSchemaType() - + ". But the schema types doesn't exist in the request"); - } - writeBundleToOutputStream(outputStream, newDocument.getBundle()); - } - mAreDocumentsMigrated = true; - } - } - - /** - * Reads the {@link Bundle} of a {@link GenericDocument} from given {@link DataInputStream}. - * - * @param inputStream The inputStream to read from - * - * @throws IOException on read failure. - * @throws EOFException if {@link java.io.InputStream} reaches the end. - */ - @NonNull - public static GenericDocument readDocumentFromInputStream( - @NonNull DataInputStream inputStream) throws IOException { - int length = inputStream.readInt(); - if (length == 0) { - throw new EOFException(); - } - byte[] serializedMessage = new byte[length]; - inputStream.read(serializedMessage); - - Parcel parcel = Parcel.obtain(); - try { - parcel.unmarshall(serializedMessage, 0, serializedMessage.length); - parcel.setDataPosition(0); - Bundle bundle = parcel.readBundle(); - return new GenericDocument(bundle); - } finally { - parcel.recycle(); - } - } - - /** - * Serializes a {@link Bundle} and writes into the given {@link DataOutputStream}. - */ - public static void writeBundleToOutputStream( - @NonNull DataOutputStream outputStream, @NonNull Bundle bundle) - throws IOException { - Parcel parcel = Parcel.obtain(); - try { - parcel.writeBundle(bundle); - byte[] serializedMessage = parcel.marshall(); - outputStream.writeInt(serializedMessage.length); - outputStream.write(serializedMessage); - } finally { - parcel.recycle(); - } - } - - @Override - public void close() throws IOException { - mMigratedFile.delete(); - } -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java deleted file mode 100644 index 82b6d62e0758c..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +++ /dev/null @@ -1,885 +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 android.annotation.CallbackExecutor; -import android.annotation.NonNull; -import android.app.appsearch.aidl.AppSearchBatchResultParcel; -import android.app.appsearch.aidl.AppSearchResultParcel; -import android.app.appsearch.aidl.IAppSearchBatchResultCallback; -import android.app.appsearch.aidl.IAppSearchManager; -import android.app.appsearch.aidl.IAppSearchResultCallback; -import android.app.appsearch.exceptions.AppSearchException; -import android.app.appsearch.util.SchemaMigrationUtil; -import android.os.Bundle; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Log; - -import com.android.internal.util.Preconditions; - -import java.io.Closeable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -/** - * Provides a connection to a single AppSearch database. - * - *

An {@link AppSearchSession} instance provides access to database operations such as - * setting a schema, adding documents, and searching. - * - *

This class is thread safe. - * - * @see GlobalSearchSession - */ -public final class AppSearchSession implements Closeable { - private static final String TAG = "AppSearchSession"; - - private final String mPackageName; - private final String mDatabaseName; - private final UserHandle mUserHandle; - private final IAppSearchManager mService; - - private boolean mIsMutated = false; - private boolean mIsClosed = false; - - /** - * Creates a search session for the client, defined by the {@code userHandle} and - * {@code packageName}. - */ - static void createSearchSession( - @NonNull AppSearchManager.SearchContext searchContext, - @NonNull IAppSearchManager service, - @NonNull UserHandle userHandle, - @NonNull String packageName, - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer> callback) { - AppSearchSession searchSession = - new AppSearchSession(service, userHandle, packageName, searchContext.mDatabaseName); - searchSession.initialize(executor, callback); - } - - // NOTE: No instance of this class should be created or returned except via initialize(). - // Once the callback.accept has been called here, the class is ready to use. - private void initialize( - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer> callback) { - try { - mService.initialize( - mPackageName, - mUserHandle, - /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), - new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - executor.execute(() -> { - AppSearchResult result = resultParcel.getResult(); - if (result.isSuccess()) { - callback.accept( - AppSearchResult.newSuccessfulResult( - AppSearchSession.this)); - } else { - callback.accept(AppSearchResult.newFailedResult(result)); - } - }); - } - }); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - private AppSearchSession(@NonNull IAppSearchManager service, @NonNull UserHandle userHandle, - @NonNull String packageName, @NonNull String databaseName) { - mService = service; - mUserHandle = userHandle; - mPackageName = packageName; - mDatabaseName = databaseName; - } - - /** - * Sets the schema that represents the organizational structure of data within the AppSearch - * database. - * - *

Upon creating an {@link AppSearchSession}, {@link #setSchema} should be called. If the - * schema needs to be updated, or it has not been previously set, then the provided schema will - * be saved and persisted to disk. Otherwise, {@link #setSchema} is handled efficiently as a - * no-op call. - * - * @param request the schema to set or update the AppSearch database to. - * @param workExecutor Executor on which to schedule heavy client-side background work such as - * transforming documents. - * @param callbackExecutor Executor on which to invoke the callback. - * @param callback Callback to receive errors resulting from setting the schema. If the - * operation succeeds, the callback will be invoked with {@code null}. - */ - public void setSchema( - @NonNull SetSchemaRequest request, - @NonNull Executor workExecutor, - @NonNull @CallbackExecutor Executor callbackExecutor, - @NonNull Consumer> callback) { - Objects.requireNonNull(request); - Objects.requireNonNull(workExecutor); - Objects.requireNonNull(callbackExecutor); - Objects.requireNonNull(callback); - Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); - List schemaBundles = new ArrayList<>(request.getSchemas().size()); - for (AppSearchSchema schema : request.getSchemas()) { - schemaBundles.add(schema.getBundle()); - } - Map> schemasVisibleToPackagesBundles = - new ArrayMap<>(request.getSchemasVisibleToPackagesInternal().size()); - for (Map.Entry> entry : - request.getSchemasVisibleToPackagesInternal().entrySet()) { - List packageIdentifierBundles = new ArrayList<>(entry.getValue().size()); - for (PackageIdentifier packageIdentifier : entry.getValue()) { - packageIdentifierBundles.add(packageIdentifier.getBundle()); - } - schemasVisibleToPackagesBundles.put(entry.getKey(), packageIdentifierBundles); - } - - // No need to trigger migration if user never set migrator - if (request.getMigrators().isEmpty()) { - setSchemaNoMigrations( - request, - schemaBundles, - schemasVisibleToPackagesBundles, - callbackExecutor, - callback); - } else { - setSchemaWithMigrations( - request, - schemaBundles, - schemasVisibleToPackagesBundles, - workExecutor, - callbackExecutor, - callback); - } - mIsMutated = true; - } - - /** - * Retrieves the schema most recently successfully provided to {@link #setSchema}. - * - * @param executor Executor on which to invoke the callback. - * @param callback Callback to receive the pending results of schema. - */ - public void getSchema( - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer> callback) { - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); - try { - mService.getSchema( - mPackageName, - mDatabaseName, - mUserHandle, - new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - executor.execute(() -> { - AppSearchResult result = resultParcel.getResult(); - if (result.isSuccess()) { - GetSchemaResponse response = - new GetSchemaResponse(result.getResultValue()); - callback.accept(AppSearchResult.newSuccessfulResult(response)); - } else { - callback.accept(AppSearchResult.newFailedResult(result)); - } - }); - } - }); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Retrieves the set of all namespaces in the current database with at least one document. - * - * @param executor Executor on which to invoke the callback. - * @param callback Callback to receive the namespaces. - */ - public void getNamespaces( - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer>> callback) { - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); - try { - mService.getNamespaces( - mPackageName, - mDatabaseName, - mUserHandle, - new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - executor.execute(() -> { - AppSearchResult> result = resultParcel.getResult(); - if (result.isSuccess()) { - Set namespaces = - new ArraySet<>(result.getResultValue()); - callback.accept( - AppSearchResult.newSuccessfulResult(namespaces)); - } else { - callback.accept(AppSearchResult.newFailedResult(result)); - } - }); - } - }); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Indexes documents into the {@link AppSearchSession} database. - * - *

Each {@link GenericDocument} object must have a {@code schemaType} field set to an {@link - * AppSearchSchema} type that has been previously registered by calling the {@link #setSchema} - * method. - * - * @param request containing documents to be indexed. - * @param executor Executor on which to invoke the callback. - * @param callback Callback to receive pending result of performing this operation. The keys - * of the returned {@link AppSearchBatchResult} are the IDs of the input - * documents. The values are {@code null} if they were successfully indexed, - * or a failed {@link AppSearchResult} otherwise. If an unexpected internal - * error occurs in the AppSearch service, - * {@link BatchResultCallback#onSystemError} will be invoked with a - * {@link Throwable}. - */ - public void put( - @NonNull PutDocumentsRequest request, - @NonNull @CallbackExecutor Executor executor, - @NonNull BatchResultCallback callback) { - Objects.requireNonNull(request); - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); - List documents = request.getGenericDocuments(); - List documentBundles = new ArrayList<>(documents.size()); - for (int i = 0; i < documents.size(); i++) { - documentBundles.add(documents.get(i).getBundle()); - } - try { - mService.putDocuments(mPackageName, mDatabaseName, documentBundles, mUserHandle, - /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), - new IAppSearchBatchResultCallback.Stub() { - @Override - public void onResult(AppSearchBatchResultParcel resultParcel) { - executor.execute(() -> callback.onResult(resultParcel.getResult())); - } - - @Override - public void onSystemError(AppSearchResultParcel resultParcel) { - executor.execute(() -> sendSystemErrorToCallback( - resultParcel.getResult(), callback)); - } - }); - mIsMutated = true; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Gets {@link GenericDocument} objects by document IDs in a namespace from the {@link - * AppSearchSession} database. - * - * @param request a request containing a namespace and IDs to get documents for. - * @param executor Executor on which to invoke the callback. - * @param callback Callback to receive the pending result of performing this operation. The keys - * of the returned {@link AppSearchBatchResult} are the input IDs. The values - * are the returned {@link GenericDocument}s on success, or a failed - * {@link AppSearchResult} otherwise. IDs that are not found will return a - * failed {@link AppSearchResult} with a result code of - * {@link AppSearchResult#RESULT_NOT_FOUND}. If an unexpected internal error - * occurs in the AppSearch service, {@link BatchResultCallback#onSystemError} - * will be invoked with a {@link Throwable}. - */ - public void getByDocumentId( - @NonNull GetByDocumentIdRequest request, - @NonNull @CallbackExecutor Executor executor, - @NonNull BatchResultCallback callback) { - Objects.requireNonNull(request); - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); - try { - mService.getDocuments( - mPackageName, - mDatabaseName, - request.getNamespace(), - new ArrayList<>(request.getIds()), - request.getProjectionsInternal(), - mUserHandle, - /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), - new IAppSearchBatchResultCallback.Stub() { - @Override - public void onResult(AppSearchBatchResultParcel resultParcel) { - executor.execute(() -> { - AppSearchBatchResult result = - resultParcel.getResult(); - AppSearchBatchResult.Builder - documentResultBuilder = - new AppSearchBatchResult.Builder<>(); - - // Translate successful results - for (Map.Entry bundleEntry : - result.getSuccesses().entrySet()) { - GenericDocument document; - try { - document = new GenericDocument(bundleEntry.getValue()); - } catch (Throwable t) { - // These documents went through validation, so how could - // this fail? We must have done something wrong. - documentResultBuilder.setFailure( - bundleEntry.getKey(), - AppSearchResult.RESULT_INTERNAL_ERROR, - t.getMessage()); - continue; - } - documentResultBuilder.setSuccess( - bundleEntry.getKey(), document); - } - - // Translate failed results - for (Map.Entry> bundleEntry : - ((Map>) - result.getFailures()).entrySet()) { - documentResultBuilder.setFailure( - bundleEntry.getKey(), - bundleEntry.getValue().getResultCode(), - bundleEntry.getValue().getErrorMessage()); - } - callback.onResult(documentResultBuilder.build()); - }); - } - - @Override - public void onSystemError(AppSearchResultParcel result) { - executor.execute( - () -> sendSystemErrorToCallback(result.getResult(), callback)); - } - }); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Retrieves documents from the open {@link AppSearchSession} that match a given query - * string and type of search provided. - * - *

Query strings can be empty, contain one term with no operators, or contain multiple terms - * and operators. - * - *

For query strings that are empty, all documents that match the {@link SearchSpec} will be - * returned. - * - *

For query strings with a single term and no operators, documents that match the provided - * query string and {@link SearchSpec} will be returned. - * - *

The following operators are supported: - * - *

    - *
  • AND (implicit) - *

    AND is an operator that matches documents that contain all provided terms. - *

    NOTE: A space between terms is treated as an "AND" operator. Explicitly - * including "AND" in a query string will treat "AND" as a term, returning documents that - * also contain "AND". - *

    Example: "apple AND banana" matches documents that contain the terms "apple", "and", - * "banana". - *

    Example: "apple banana" matches documents that contain both "apple" and "banana". - *

    Example: "apple banana cherry" matches documents that contain "apple", "banana", and - * "cherry". - *

  • OR - *

    OR is an operator that matches documents that contain any provided term. - *

    Example: "apple OR banana" matches documents that contain either "apple" or - * "banana". - *

    Example: "apple OR banana OR cherry" matches documents that contain any of "apple", - * "banana", or "cherry". - *

  • Exclusion (-) - *

    Exclusion (-) is an operator that matches documents that do not contain the - * provided term. - *

    Example: "-apple" matches documents that do not contain "apple". - *

  • Grouped Terms - *

    For queries that require multiple operators and terms, terms can be grouped into - * subqueries. Subqueries are contained within an open "(" and close ")" parenthesis. - *

    Example: "(donut OR bagel) (coffee OR tea)" matches documents that contain either - * "donut" or "bagel" and either "coffee" or "tea". - *

  • Property Restricts - *

    For queries that require a term to match a specific {@link AppSearchSchema} property - * of a document, a ":" must be included between the property name and the term. - *

    Example: "subject:important" matches documents that contain the term "important" in - * the "subject" property. - *

- * - *

Additional search specifications, such as filtering by {@link AppSearchSchema} type or - * adding projection, can be set by calling the corresponding {@link SearchSpec.Builder} setter. - * - *

This method is lightweight. The heavy work will be done in {@link - * SearchResults#getNextPage}. - * - * @param queryExpression query string to search. - * @param searchSpec spec for setting document filters, adding projection, setting term match - * type, etc. - * @return a {@link SearchResults} object for retrieved matched documents. - */ - @NonNull - public SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec) { - Objects.requireNonNull(queryExpression); - Objects.requireNonNull(searchSpec); - Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); - return new SearchResults(mService, mPackageName, mDatabaseName, queryExpression, - searchSpec, mUserHandle); - } - - /** - * Reports usage of a particular document by namespace and ID. - * - *

A usage report represents an event in which a user interacted with or viewed a document. - * - *

For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency - * metrics for that particular document. These metrics are used for ordering {@link #search} - * results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and {@link - * SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies. - * - *

Reporting usage of a document is optional. - * - * @param request The usage reporting request. - * @param executor Executor on which to invoke the callback. - * @param callback Callback to receive errors. If the operation succeeds, the callback will be - * invoked with {@code null}. - */ - public void reportUsage( - @NonNull ReportUsageRequest request, - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer> callback) { - Objects.requireNonNull(request); - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); - try { - mService.reportUsage( - mPackageName, - mDatabaseName, - request.getNamespace(), - request.getDocumentId(), - request.getUsageTimestampMillis(), - /*systemUsage=*/ false, - mUserHandle, - new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - executor.execute(() -> callback.accept(resultParcel.getResult())); - } - }); - mIsMutated = true; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Removes {@link GenericDocument} objects by document IDs in a namespace from the {@link - * AppSearchSession} database. - * - *

Removed documents will no longer be surfaced by {@link #search} or {@link - * #getByDocumentId} calls. - * - *

Once the database crosses the document count or byte usage threshold, removed documents - * will be deleted from disk. - * - * @param request {@link RemoveByDocumentIdRequest} with IDs in a namespace to remove from the - * index. - * @param executor Executor on which to invoke the callback. - * @param callback Callback to receive the pending result of performing this operation. The keys - * of the returned {@link AppSearchBatchResult} are the input document IDs. The - * values are {@code null} on success, or a failed {@link AppSearchResult} - * otherwise. IDs that are not found will return a failed - * {@link AppSearchResult} with a result code of - * {@link AppSearchResult#RESULT_NOT_FOUND}. If an unexpected internal error - * occurs in the AppSearch service, {@link BatchResultCallback#onSystemError} - * will be invoked with a {@link Throwable}. - */ - public void remove( - @NonNull RemoveByDocumentIdRequest request, - @NonNull @CallbackExecutor Executor executor, - @NonNull BatchResultCallback callback) { - Objects.requireNonNull(request); - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); - try { - mService.removeByDocumentId( - mPackageName, - mDatabaseName, - request.getNamespace(), - new ArrayList<>(request.getIds()), - mUserHandle, - /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), - new IAppSearchBatchResultCallback.Stub() { - @Override - public void onResult(AppSearchBatchResultParcel resultParcel) { - executor.execute(() -> callback.onResult(resultParcel.getResult())); - } - - @Override - public void onSystemError(AppSearchResultParcel resultParcel) { - executor.execute(() -> sendSystemErrorToCallback( - resultParcel.getResult(), callback)); - } - }); - mIsMutated = true; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they - * match the {@code queryExpression} in given namespaces and schemaTypes which is set via {@link - * SearchSpec.Builder#addFilterNamespaces} and {@link SearchSpec.Builder#addFilterSchemas}. - * - *

An empty {@code queryExpression} matches all documents. - * - *

An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in the - * current database. - * - * @param queryExpression Query String to search. - * @param searchSpec Spec containing schemaTypes, namespaces and query expression indicates how - * document will be removed. All specific about how to scoring, ordering, snippeting and - * resulting will be ignored. - * @param executor Executor on which to invoke the callback. - * @param callback Callback to receive errors resulting from removing the documents. If - * the operation succeeds, the callback will be invoked with - * {@code null}. - */ - public void remove( - @NonNull String queryExpression, - @NonNull SearchSpec searchSpec, - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer> callback) { - Objects.requireNonNull(queryExpression); - Objects.requireNonNull(searchSpec); - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); - try { - mService.removeByQuery( - mPackageName, - mDatabaseName, - queryExpression, - searchSpec.getBundle(), - mUserHandle, - /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), - new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - executor.execute(() -> callback.accept(resultParcel.getResult())); - } - }); - mIsMutated = true; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Gets the storage info for this {@link AppSearchSession} database. - * - *

This may take time proportional to the number of documents and may be inefficient to call - * repeatedly. - * - * @param executor Executor on which to invoke the callback. - * @param callback Callback to receive the storage info. - */ - public void getStorageInfo( - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer> callback) { - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); - try { - mService.getStorageInfo( - mPackageName, - mDatabaseName, - mUserHandle, - new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - executor.execute(() -> { - AppSearchResult result = resultParcel.getResult(); - if (result.isSuccess()) { - StorageInfo response = new StorageInfo(result.getResultValue()); - callback.accept(AppSearchResult.newSuccessfulResult(response)); - } else { - callback.accept(AppSearchResult.newFailedResult(result)); - } - }); - } - }); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Closes the {@link AppSearchSession} to persist all schema and document updates, - * additions, and deletes to disk. - */ - @Override - public void close() { - if (mIsMutated && !mIsClosed) { - try { - mService.persistToDisk( - mPackageName, - mUserHandle, - /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime()); - mIsClosed = true; - } catch (RemoteException e) { - Log.e(TAG, "Unable to close the AppSearchSession", e); - } - } - } - - /** - * Set schema to Icing for no-migration scenario. - * - *

We only need one time {@link #setSchema} call for no-migration scenario by using the - * forceoverride in the request. - */ - private void setSchemaNoMigrations( - @NonNull SetSchemaRequest request, - @NonNull List schemaBundles, - @NonNull Map> schemasVisibleToPackagesBundles, - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer> callback) { - try { - mService.setSchema( - mPackageName, - mDatabaseName, - schemaBundles, - new ArrayList<>(request.getSchemasNotDisplayedBySystem()), - schemasVisibleToPackagesBundles, - request.isForceOverride(), - request.getVersion(), - mUserHandle, - /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), - new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - executor.execute(() -> { - AppSearchResult result = resultParcel.getResult(); - if (result.isSuccess()) { - try { - SetSchemaResponse setSchemaResponse = - new SetSchemaResponse(result.getResultValue()); - if (!request.isForceOverride()) { - // Throw exception if there is any deleted types or - // incompatible types. That's the only case we swallowed - // in the AppSearchImpl#setSchema(). - SchemaMigrationUtil.checkDeletedAndIncompatible( - setSchemaResponse.getDeletedTypes(), - setSchemaResponse.getIncompatibleTypes()); - } - callback.accept(AppSearchResult - .newSuccessfulResult(setSchemaResponse)); - } catch (Throwable t) { - callback.accept(AppSearchResult.throwableToFailedResult(t)); - } - } else { - callback.accept(AppSearchResult.newFailedResult(result)); - } - }); - } - }); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Set schema to Icing for migration scenario. - * - *

First time {@link #setSchema} call with forceOverride is false gives us all incompatible - * changes. After trigger migrations, the second time call {@link #setSchema} will actually - * apply the changes. - */ - private void setSchemaWithMigrations( - @NonNull SetSchemaRequest request, - @NonNull List schemaBundles, - @NonNull Map> schemasVisibleToPackagesBundles, - @NonNull Executor workExecutor, - @NonNull @CallbackExecutor Executor callbackExecutor, - @NonNull Consumer> callback) { - workExecutor.execute(() -> { - try { - // Migration process - // 1. Validate and retrieve all active migrators. - CompletableFuture> getSchemaFuture = - new CompletableFuture<>(); - getSchema(callbackExecutor, getSchemaFuture::complete); - AppSearchResult getSchemaResult = getSchemaFuture.get(); - if (!getSchemaResult.isSuccess()) { - callbackExecutor.execute(() -> - callback.accept(AppSearchResult.newFailedResult(getSchemaResult))); - return; - } - GetSchemaResponse getSchemaResponse = getSchemaResult.getResultValue(); - int currentVersion = getSchemaResponse.getVersion(); - int finalVersion = request.getVersion(); - Map activeMigrators = SchemaMigrationUtil.getActiveMigrators( - getSchemaResponse.getSchemas(), request.getMigrators(), currentVersion, - finalVersion); - - // No need to trigger migration if no migrator is active. - if (activeMigrators.isEmpty()) { - setSchemaNoMigrations(request, schemaBundles, schemasVisibleToPackagesBundles, - callbackExecutor, callback); - return; - } - - // 2. SetSchema with forceOverride=false, to retrieve the list of - // incompatible/deleted types. - CompletableFuture> setSchemaFuture = - new CompletableFuture<>(); - mService.setSchema( - mPackageName, - mDatabaseName, - schemaBundles, - new ArrayList<>(request.getSchemasNotDisplayedBySystem()), - schemasVisibleToPackagesBundles, - /*forceOverride=*/ false, - request.getVersion(), - mUserHandle, - /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), - new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - setSchemaFuture.complete(resultParcel.getResult()); - } - }); - AppSearchResult setSchemaResult = setSchemaFuture.get(); - if (!setSchemaResult.isSuccess()) { - callbackExecutor.execute(() -> - callback.accept(AppSearchResult.newFailedResult(setSchemaResult))); - return; - } - SetSchemaResponse setSchemaResponse = - new SetSchemaResponse(setSchemaResult.getResultValue()); - - // 3. If forceOverride is false, check that all incompatible types will be migrated. - // If some aren't we must throw an error, rather than proceeding and deleting those - // types. - if (!request.isForceOverride()) { - SchemaMigrationUtil.checkDeletedAndIncompatibleAfterMigration(setSchemaResponse, - activeMigrators.keySet()); - } - - try (AppSearchMigrationHelper migrationHelper = new AppSearchMigrationHelper( - mService, mUserHandle, mPackageName, mDatabaseName, request.getSchemas())) { - - // 4. Trigger migration for all migrators. - // TODO(b/177266929) trigger migration for all types together rather than - // separately. - for (Map.Entry entry : activeMigrators.entrySet()) { - migrationHelper.queryAndTransform(/*schemaType=*/ entry.getKey(), - /*migrator=*/ entry.getValue(), currentVersion, - finalVersion); - } - - // 5. SetSchema a second time with forceOverride=true if the first attempted - // failed. - if (!setSchemaResponse.getIncompatibleTypes().isEmpty() - || !setSchemaResponse.getDeletedTypes().isEmpty()) { - CompletableFuture> setSchema2Future = - new CompletableFuture<>(); - // only trigger second setSchema() call if the first one is fail. - mService.setSchema( - mPackageName, - mDatabaseName, - schemaBundles, - new ArrayList<>(request.getSchemasNotDisplayedBySystem()), - schemasVisibleToPackagesBundles, - /*forceOverride=*/ true, - request.getVersion(), - mUserHandle, - /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), - new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - setSchema2Future.complete(resultParcel.getResult()); - } - }); - AppSearchResult setSchema2Result = setSchema2Future.get(); - if (!setSchema2Result.isSuccess()) { - // we failed to set the schema in second time with forceOverride = true, - // which is an impossible case. Since we only swallow the incompatible - // error in the first setSchema call, all other errors will be thrown at - // the first time. - callbackExecutor.execute(() -> callback.accept( - AppSearchResult.newFailedResult(setSchema2Result))); - return; - } - } - - SetSchemaResponse.Builder responseBuilder = setSchemaResponse.toBuilder() - .addMigratedTypes(activeMigrators.keySet()); - - // 6. Put all the migrated documents into the index, now that the new schema is - // set. - AppSearchResult putResult = - migrationHelper.putMigratedDocuments(responseBuilder); - callbackExecutor.execute(() -> callback.accept(putResult)); - } - } catch (Throwable t) { - callbackExecutor.execute(() -> callback.accept( - AppSearchResult.throwableToFailedResult(t))); - } - }); - } - - /** - * Calls {@link BatchResultCallback#onSystemError} with a throwable derived from the given - * failed {@link AppSearchResult}. - * - *

The {@link AppSearchResult} generally comes from - * {@link IAppSearchBatchResultCallback#onSystemError}. - * - *

This method should be called from the callback executor thread. - */ - private void sendSystemErrorToCallback( - @NonNull AppSearchResult failedResult, @NonNull BatchResultCallback callback) { - Preconditions.checkArgument(!failedResult.isSuccess()); - Throwable throwable = new AppSearchException( - failedResult.getResultCode(), failedResult.getErrorMessage()); - callback.onSystemError(throwable); - } -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/BatchResultCallback.java b/apex/appsearch/framework/java/android/app/appsearch/BatchResultCallback.java deleted file mode 100644 index 28f8a7a78f4af..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/BatchResultCallback.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 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 android.annotation.NonNull; -import android.annotation.Nullable; - -/** - * The callback interface to return {@link AppSearchBatchResult}. - * - * @param The type of the keys for {@link AppSearchBatchResult#getSuccesses} and - * {@link AppSearchBatchResult#getFailures}. - * @param The type of result objects associated with the keys. - */ -public interface BatchResultCallback { - - /** - * Called when {@link AppSearchBatchResult} results are ready. - * - * @param result The result of the executed request. - */ - void onResult(@NonNull AppSearchBatchResult result); - - /** - * Called when a system error occurs. - * - *

This method is only called the infrastructure is fundamentally broken or unavailable, such - * that none of the requests could be started. For example, it will be called if the AppSearch - * service unexpectedly fails to initialize and can't be recovered by any means, or if - * communicating to the server over Binder fails (e.g. system service crashed or device is - * rebooting). - * - *

The error is not expected to be recoverable and there is no specific recommended action - * other than displaying a permanent message to the user. - * - *

Normal errors that are caused by invalid inputs or recoverable/retriable situations - * are reported associated with the input that caused them via the {@link #onResult} method. - * - * @param throwable an exception describing the system error - */ - default void onSystemError(@Nullable Throwable throwable) { - throw new RuntimeException("Unrecoverable system error", throwable); - } -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java deleted file mode 100644 index 130e442c00001..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java +++ /dev/null @@ -1,200 +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 android.annotation.CallbackExecutor; -import android.annotation.NonNull; -import android.app.appsearch.aidl.AppSearchResultParcel; -import android.app.appsearch.aidl.IAppSearchManager; -import android.app.appsearch.aidl.IAppSearchResultCallback; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.util.Log; - -import com.android.internal.util.Preconditions; - -import java.io.Closeable; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -/** - * Provides a connection to all AppSearch databases the querying application has been granted access - * to. - * - *

This class is thread safe. - * - * @see AppSearchSession - */ -public class GlobalSearchSession implements Closeable { - private static final String TAG = "AppSearchGlobalSearchSe"; - - private final String mPackageName; - private final UserHandle mUserHandle; - private final IAppSearchManager mService; - - private boolean mIsMutated = false; - private boolean mIsClosed = false; - - /** - * Creates a search session for the client, defined by the {@code userHandle} and - * {@code packageName}. - */ - static void createGlobalSearchSession( - @NonNull IAppSearchManager service, - @NonNull UserHandle userHandle, - @NonNull String packageName, - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer> callback) { - GlobalSearchSession globalSearchSession = new GlobalSearchSession(service, userHandle, - packageName); - globalSearchSession.initialize(executor, callback); - } - - // NOTE: No instance of this class should be created or returned except via initialize(). - // Once the callback.accept has been called here, the class is ready to use. - private void initialize( - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer> callback) { - try { - mService.initialize( - mPackageName, - mUserHandle, - /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(), - new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - executor.execute(() -> { - AppSearchResult result = resultParcel.getResult(); - if (result.isSuccess()) { - callback.accept( - AppSearchResult.newSuccessfulResult( - GlobalSearchSession.this)); - } else { - callback.accept(AppSearchResult.newFailedResult(result)); - } - }); - } - }); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - private GlobalSearchSession(@NonNull IAppSearchManager service, @NonNull UserHandle userHandle, - @NonNull String packageName) { - mService = service; - mUserHandle = userHandle; - mPackageName = packageName; - } - - /** - * Retrieves documents from all AppSearch databases that the querying application has access to. - * - *

Applications can be granted access to documents by specifying {@link - * SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} when building a schema. - * - *

Document access can also be granted to system UIs by specifying {@link - * SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} when building a schema. - * - *

See {@link AppSearchSession#search} for a detailed explanation on forming a query string. - * - *

This method is lightweight. The heavy work will be done in {@link - * SearchResults#getNextPage}. - * - * @param queryExpression query string to search. - * @param searchSpec spec for setting document filters, adding projection, setting term match - * type, etc. - * @return a {@link SearchResults} object for retrieved matched documents. - */ - @NonNull - public SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec) { - Objects.requireNonNull(queryExpression); - Objects.requireNonNull(searchSpec); - Preconditions.checkState(!mIsClosed, "GlobalSearchSession has already been closed"); - return new SearchResults(mService, mPackageName, /*databaseName=*/null, queryExpression, - searchSpec, mUserHandle); - } - - /** - * Reports that a particular document has been used from a system surface. - * - *

See {@link AppSearchSession#reportUsage} for a general description of document usage, as - * well as an API that can be used by the app itself. - * - *

Usage reported via this method is accounted separately from usage reported via - * {@link AppSearchSession#reportUsage} and may be accessed using the constants - * {@link SearchSpec#RANKING_STRATEGY_SYSTEM_USAGE_COUNT} and - * {@link SearchSpec#RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP}. - * - * @param request The usage reporting request. - * @param executor Executor on which to invoke the callback. - * @param callback Callback to receive errors. If the operation succeeds, the callback will be - * invoked with an {@link AppSearchResult} whose value is {@code null}. The - * callback will be invoked with an {@link AppSearchResult} of - * {@link AppSearchResult#RESULT_SECURITY_ERROR} if this API is invoked by an - * app which is not part of the system. - */ - public void reportSystemUsage( - @NonNull ReportSystemUsageRequest request, - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer> callback) { - Objects.requireNonNull(request); - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - Preconditions.checkState(!mIsClosed, "GlobalSearchSession has already been closed"); - try { - mService.reportUsage( - request.getPackageName(), - request.getDatabaseName(), - request.getNamespace(), - request.getDocumentId(), - request.getUsageTimestampMillis(), - /*systemUsage=*/ true, - mUserHandle, - new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - executor.execute(() -> callback.accept(resultParcel.getResult())); - } - }); - mIsMutated = true; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Closes the {@link GlobalSearchSession}. Persists all mutations, including usage reports, to - * disk. - */ - @Override - public void close() { - if (mIsMutated && !mIsClosed) { - try { - mService.persistToDisk( - mPackageName, - mUserHandle, - /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime()); - mIsClosed = true; - } catch (RemoteException e) { - Log.e(TAG, "Unable to close the GlobalSearchSession", e); - } - } - } -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java deleted file mode 100644 index 6dfa01f6e6805..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java +++ /dev/null @@ -1,175 +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 android.annotation.CallbackExecutor; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.aidl.AppSearchResultParcel; -import android.app.appsearch.aidl.IAppSearchManager; -import android.app.appsearch.aidl.IAppSearchResultCallback; -import android.os.Bundle; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.util.Log; - -import com.android.internal.util.Preconditions; - -import java.io.Closeable; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -/** - * Encapsulates results of a search operation. - * - *

Each {@link AppSearchSession#search} operation returns a list of {@link SearchResult} objects, - * referred to as a "page", limited by the size configured by {@link - * SearchSpec.Builder#setResultCountPerPage}. - * - *

To fetch a page of results, call {@link #getNextPage}. - * - *

All instances of {@link SearchResults} must call {@link SearchResults#close()} after the - * results are fetched. - * - *

This class is not thread safe. - */ -public class SearchResults implements Closeable { - private static final String TAG = "SearchResults"; - - private final IAppSearchManager mService; - - // The package name of the caller. - private final String mPackageName; - - // The database name to search over. If null, this will search over all database names. - @Nullable - private final String mDatabaseName; - - private final String mQueryExpression; - - private final SearchSpec mSearchSpec; - - private final UserHandle mUserHandle; - - private long mNextPageToken; - - private boolean mIsFirstLoad = true; - - private boolean mIsClosed = false; - - SearchResults( - @NonNull IAppSearchManager service, - @NonNull String packageName, - @Nullable String databaseName, - @NonNull String queryExpression, - @NonNull SearchSpec searchSpec, - @NonNull UserHandle userHandle) { - mService = Objects.requireNonNull(service); - mPackageName = packageName; - mDatabaseName = databaseName; - mQueryExpression = Objects.requireNonNull(queryExpression); - mSearchSpec = Objects.requireNonNull(searchSpec); - mUserHandle = Objects.requireNonNull(userHandle); - } - - /** - * Retrieves the next page of {@link SearchResult} objects. - * - *

The page size is configured by {@link SearchSpec.Builder#setResultCountPerPage}. - * - *

Continue calling this method to access results until it returns an empty list, signifying - * there are no more results. - * - * @param executor Executor on which to invoke the callback. - * @param callback Callback to receive the pending result of performing this operation. - */ - public void getNextPage( - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer>> callback) { - Objects.requireNonNull(executor); - Objects.requireNonNull(callback); - Preconditions.checkState(!mIsClosed, "SearchResults has already been closed"); - try { - if (mIsFirstLoad) { - mIsFirstLoad = false; - long binderCallStartTimeMillis = SystemClock.elapsedRealtime(); - if (mDatabaseName == null) { - // Global query, there's no one package-database combination to check. - mService.globalQuery(mPackageName, mQueryExpression, - mSearchSpec.getBundle(), mUserHandle, - binderCallStartTimeMillis, - wrapCallback(executor, callback)); - } else { - // Normal local query, pass in specified database. - mService.query(mPackageName, mDatabaseName, mQueryExpression, - mSearchSpec.getBundle(), mUserHandle, - binderCallStartTimeMillis, - wrapCallback(executor, callback)); - } - } else { - mService.getNextPage(mPackageName, mNextPageToken, mUserHandle, - wrapCallback(executor, callback)); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - @Override - public void close() { - if (!mIsClosed) { - try { - mService.invalidateNextPageToken(mPackageName, mNextPageToken, mUserHandle); - mIsClosed = true; - } catch (RemoteException e) { - Log.e(TAG, "Unable to close the SearchResults", e); - } - } - } - - private IAppSearchResultCallback wrapCallback( - @NonNull @CallbackExecutor Executor executor, - @NonNull Consumer>> callback) { - return new IAppSearchResultCallback.Stub() { - @Override - public void onResult(AppSearchResultParcel resultParcel) { - executor.execute(() -> invokeCallback(resultParcel.getResult(), callback)); - } - }; - } - - private void invokeCallback( - @NonNull AppSearchResult searchResultPageResult, - @NonNull Consumer>> callback) { - if (searchResultPageResult.isSuccess()) { - try { - SearchResultPage searchResultPage = - new SearchResultPage(searchResultPageResult.getResultValue()); - mNextPageToken = searchResultPage.getNextPageToken(); - callback.accept(AppSearchResult.newSuccessfulResult( - searchResultPage.getResults())); - } catch (Throwable t) { - callback.accept(AppSearchResult.throwableToFailedResult(t)); - } - } else { - callback.accept(AppSearchResult.newFailedResult(searchResultPageResult)); - } - } -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.aidl deleted file mode 100644 index 89908cde582d2..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright 2021, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.app.appsearch.aidl; - -/** {@hide} */ -parcelable AppSearchBatchResultParcel; diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.java b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.java deleted file mode 100644 index b0cc10c2f1c16..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchBatchResultParcel.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 android.app.appsearch.aidl; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchBatchResult; -import android.app.appsearch.AppSearchResult; -import android.os.Bundle; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Map; -import java.util.Objects; - -/** - * Parcelable wrapper around {@link AppSearchBatchResult}. - * - *

{@link AppSearchBatchResult} can contain any type of key and value, including non-parcelable - * values. For the specific case of sending {@link AppSearchBatchResult} across Binder, this class - * wraps an {@link AppSearchBatchResult} that has String keys and Parcelable values. It provides - * parcelability of the whole structure. - * - * @param The type of result object for successful calls. Must be a parcelable type. - * @hide - */ -public final class AppSearchBatchResultParcel implements Parcelable { - private final AppSearchBatchResult mResult; - - /** Creates a new {@link AppSearchBatchResultParcel} from the given result. */ - public AppSearchBatchResultParcel(@NonNull AppSearchBatchResult result) { - mResult = Objects.requireNonNull(result); - } - - private AppSearchBatchResultParcel(@NonNull Parcel in) { - Bundle bundle = in.readBundle(); - AppSearchBatchResult.Builder builder = - new AppSearchBatchResult.Builder<>(); - for (String key : bundle.keySet()) { - AppSearchResultParcel resultParcel = bundle.getParcelable(key); - builder.setResult(key, resultParcel.getResult()); - } - mResult = builder.build(); - } - - @NonNull - public AppSearchBatchResult getResult() { - return mResult; - } - - /** @hide */ - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - Bundle bundle = new Bundle(); - for (Map.Entry> entry - : mResult.getAll().entrySet()) { - bundle.putParcelable(entry.getKey(), new AppSearchResultParcel<>(entry.getValue())); - } - dest.writeBundle(bundle); - } - - /** @hide */ - @Override - public int describeContents() { - return 0; - } - - /** @hide */ - @NonNull - public static final Creator> CREATOR = - new Creator>() { - @NonNull - @Override - public AppSearchBatchResultParcel createFromParcel(@NonNull Parcel in) { - return new AppSearchBatchResultParcel<>(in); - } - - @NonNull - @Override - public AppSearchBatchResultParcel[] newArray(int size) { - return new AppSearchBatchResultParcel[size]; - } - }; -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.aidl deleted file mode 100644 index 4e35bd5d72627..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright 2021, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.app.appsearch.aidl; - -/** {@hide} */ -parcelable AppSearchResultParcel; diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.java b/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.java deleted file mode 100644 index 8b137d3bf926b..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/aidl/AppSearchResultParcel.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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 android.app.appsearch.aidl; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchResult; -import android.os.Parcel; -import android.os.Parcelable; - -import java.util.Objects; - -/** - * Parcelable wrapper around {@link AppSearchResult}. - * - *

{@link AppSearchResult} can contain any value, including non-parcelable values. For the - * specific case of sending {@link AppSearchResult} across Binder, this class wraps an - * {@link AppSearchResult} that contains a parcelable type and provides parcelability of the whole - * structure. - * - * @param The type of result object for successful calls. Must be a parcelable type. - * @hide - */ -public final class AppSearchResultParcel implements Parcelable { - private final AppSearchResult mResult; - - /** Creates a new {@link AppSearchResultParcel} from the given result. */ - public AppSearchResultParcel(@NonNull AppSearchResult result) { - mResult = Objects.requireNonNull(result); - } - - private AppSearchResultParcel(@NonNull Parcel in) { - int resultCode = in.readInt(); - ValueType resultValue = (ValueType) in.readValue(/*loader=*/ null); - String errorMessage = in.readString(); - if (resultCode == AppSearchResult.RESULT_OK) { - mResult = AppSearchResult.newSuccessfulResult(resultValue); - } else { - mResult = AppSearchResult.newFailedResult(resultCode, errorMessage); - } - } - - @NonNull - public AppSearchResult getResult() { - return mResult; - } - - /** @hide */ - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mResult.getResultCode()); - if (mResult.isSuccess()) { - dest.writeValue(mResult.getResultValue()); - } else { - dest.writeValue(null); - } - dest.writeString(mResult.getErrorMessage()); - } - - /** @hide */ - @Override - public int describeContents() { - return 0; - } - - /** @hide */ - @NonNull - public static final Creator> CREATOR = - new Creator>() { - @NonNull - @Override - public AppSearchResultParcel createFromParcel(@NonNull Parcel in) { - return new AppSearchResultParcel<>(in); - } - - @NonNull - @Override - public AppSearchResultParcel[] newArray(int size) { - return new AppSearchResultParcel[size]; - } - }; -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchBatchResultCallback.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchBatchResultCallback.aidl deleted file mode 100644 index 1fe19cc253e83..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchBatchResultCallback.aidl +++ /dev/null @@ -1,25 +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.aidl; - -import android.app.appsearch.aidl.AppSearchBatchResultParcel; -import android.app.appsearch.aidl.AppSearchResultParcel; - -/** {@hide} */ -oneway interface IAppSearchBatchResultCallback { - void onResult(in AppSearchBatchResultParcel resultParcel); - void onSystemError(in AppSearchResultParcel resultParcel); -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl deleted file mode 100644 index a2f545f791fd7..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchManager.aidl +++ /dev/null @@ -1,371 +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.aidl; - -import android.os.Bundle; -import android.os.UserHandle; - -import android.app.appsearch.aidl.IAppSearchBatchResultCallback; -import android.app.appsearch.aidl.IAppSearchResultCallback; -import android.os.ParcelFileDescriptor; - -/** {@hide} */ -interface IAppSearchManager { - /** - * Updates the AppSearch schema for this database. - * - * @param packageName The name of the package that owns this schema. - * @param databaseName The name of the database where this schema lives. - * @param schemaBundles List of {@link AppSearchSchema} bundles. - * @param schemasNotDisplayedBySystem Schema types that should not be surfaced on platform - * surfaces. - * @param schemasVisibleToPackagesBundles Schema types that are visible to the specified - * packages. The value List contains PackageIdentifier Bundles. - * @param forceOverride Whether to apply the new schema even if it is incompatible. All - * incompatible documents will be deleted. - * @param schemaVersion The overall schema version number of the request. - * @param userHandle Handle of the calling user - * @param binderCallStartTimeMillis start timestamp of binder call in Millis - * @param callback {@link IAppSearchResultCallback#onResult} will be called with an - * {@link AppSearchResult}<{@link Bundle}>, where the value are - * {@link SetSchemaResponse} bundle. - */ - void setSchema( - in String packageName, - in String databaseName, - in List schemaBundles, - in List schemasNotDisplayedBySystem, - in Map> schemasVisibleToPackagesBundles, - boolean forceOverride, - in int schemaVersion, - in UserHandle userHandle, - in long binderCallStartTimeMillis, - in IAppSearchResultCallback callback); - - /** - * Retrieves the AppSearch schema for this database. - * - * @param packageName The name of the package that owns the schema. - * @param databaseName The name of the database to retrieve. - * @param userHandle Handle of the calling user - * @param callback {@link IAppSearchResultCallback#onResult} will be called with an - * {@link AppSearchResult}<{@link Bundle}> where the bundle is a GetSchemaResponse. - */ - void getSchema( - in String packageName, - in String databaseName, - in UserHandle userHandle, - in IAppSearchResultCallback callback); - - /** - * Retrieves the set of all namespaces in the current database with at least one document. - * - * @param packageName The name of the package that owns the schema. - * @param databaseName The name of the database to retrieve. - * @param userHandle Handle of the calling user - * @param callback {@link IAppSearchResultCallback#onResult} will be called with an - * {@link AppSearchResult}<{@link List}<{@link String}>>. - */ - void getNamespaces( - in String packageName, - in String databaseName, - in UserHandle userHandle, - in IAppSearchResultCallback callback); - - /** - * Inserts documents into the index. - * - * @param packageName The name of the package that owns this document. - * @param databaseName The name of the database where this document lives. - * @param documentBundes List of GenericDocument bundles. - * @param userHandle Handle of the calling user - * @param binderCallStartTimeMillis start timestamp of binder call in Millis - * @param callback - * If the call fails to start, {@link IAppSearchBatchResultCallback#onSystemError} - * will be called with the cause throwable. Otherwise, - * {@link IAppSearchBatchResultCallback#onResult} will be called with an - * {@link AppSearchBatchResult}<{@link String}, {@link Void}> - * where the keys are document IDs, and the values are {@code null}. - */ - void putDocuments( - in String packageName, - in String databaseName, - in List documentBundles, - in UserHandle userHandle, - in long binderCallStartTimeMillis, - in IAppSearchBatchResultCallback callback); - - /** - * Retrieves documents from the index. - * - * @param packageName The name of the package that owns this document. - * @param databaseName The databaseName this document resides in. - * @param namespace The namespace this document resides in. - * @param ids The IDs of the documents to retrieve - * @param typePropertyPaths A map of schema type to a list of property paths to return in the - * result. - * @param userHandle Handle of the calling user - * @param binderCallStartTimeMillis start timestamp of binder call in Millis - * @param callback - * If the call fails to start, {@link IAppSearchBatchResultCallback#onSystemError} - * will be called with the cause throwable. Otherwise, - * {@link IAppSearchBatchResultCallback#onResult} will be called with an - * {@link AppSearchBatchResult}<{@link String}, {@link Bundle}> - * where the keys are document IDs, and the values are Document bundles. - */ - void getDocuments( - in String packageName, - in String databaseName, - in String namespace, - in List ids, - in Map> typePropertyPaths, - in UserHandle userHandle, - in long binderCallStartTimeMillis, - in IAppSearchBatchResultCallback callback); - - /** - * Searches a document based on a given specifications. - * - * @param packageName The name of the package to query over. - * @param databaseName The databaseName this query for. - * @param queryExpression String to search for - * @param searchSpecBundle SearchSpec bundle - * @param userHandle Handle of the calling user - * @param binderCallStartTimeMillis start timestamp of binder call in Millis - * @param callback {@link AppSearchResult}<{@link Bundle}> of performing this - * operation. - */ - void query( - in String packageName, - in String databaseName, - in String queryExpression, - in Bundle searchSpecBundle, - in UserHandle userHandle, - in long binderCallStartTimeMillis, - in IAppSearchResultCallback callback); - - /** - * Executes a global query, i.e. over all permitted databases, against the AppSearch index and - * returns results. - * - * @param packageName The name of the package making the query. - * @param queryExpression String to search for - * @param searchSpecBundle SearchSpec bundle - * @param userHandle Handle of the calling user - * @param binderCallStartTimeMillis start timestamp of binder call in Millis - * @param callback {@link AppSearchResult}<{@link Bundle}> of performing this - * operation. - */ - void globalQuery( - in String packageName, - in String queryExpression, - in Bundle searchSpecBundle, - in UserHandle userHandle, - in long binderCallStartTimeMillis, - in IAppSearchResultCallback callback); - - /** - * Fetches the next page of results of a previously executed query. Results can be empty if - * next-page token is invalid or all pages have been returned. - * - * @param packageName The name of the package to persist to disk for. - * @param nextPageToken The token of pre-loaded results of previously executed query. - * @param userHandle Handle of the calling user - * @param callback {@link AppSearchResult}<{@link Bundle}> of performing this - * operation. - */ - void getNextPage( - in String packageName, - in long nextPageToken, - in UserHandle userHandle, - in IAppSearchResultCallback callback); - - /** - * Invalidates the next-page token so that no more results of the related query can be returned. - * - * @param packageName The name of the package to persist to disk for. - * @param nextPageToken The token of pre-loaded results of previously executed query to be - * Invalidated. - * @param userHandle Handle of the calling user - */ - void invalidateNextPageToken( - in String packageName, - in long nextPageToken, - in UserHandle userHandle); - - /** - * Searches a document based on a given specifications. - * - *

Documents will be save to the given ParcelFileDescriptor - * - * @param packageName The name of the package to query over. - * @param databaseName The databaseName this query for. - * @param fileDescriptor The ParcelFileDescriptor where documents should be written to. - * @param queryExpression String to search for. - * @param searchSpecBundle SearchSpec bundle. - * @param userHandle Handle of the calling user. - * @param callback {@link IAppSearchResultCallback#onResult} will be called with an - * {@link AppSearchResult}<{@code Void}>. - */ - void writeQueryResultsToFile( - in String packageName, - in String databaseName, - in ParcelFileDescriptor fileDescriptor, - in String queryExpression, - in Bundle searchSpecBundle, - in UserHandle userHandle, - in IAppSearchResultCallback callback); - - /** - * Inserts documents from the given file into the index. - * - * @param packageName The name of the package that owns this document. - * @param databaseName The name of the database where this document lives. - * @param fileDescriptor The ParcelFileDescriptor where documents should be read from. - * @param userHandle Handle of the calling user. - * @param callback {@link IAppSearchResultCallback#onResult} will be called with an - * {@link AppSearchResult}<{@link List}<{@link Bundle}>>, where the value are - * MigrationFailure bundles. - */ - void putDocumentsFromFile( - in String packageName, - in String databaseName, - in ParcelFileDescriptor fileDescriptor, - in UserHandle userHandle, - in IAppSearchResultCallback callback); - - /** - * Reports usage of a particular document by namespace and id. - * - *

A usage report represents an event in which a user interacted with or viewed a document. - * - *

For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency - * metrics for that particular document. These metrics are used for ordering {@link #query} - * results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and - * {@link SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies. - * - *

Reporting usage of a document is optional. - * - * @param packageName The name of the package that owns this document. - * @param databaseName The name of the database to report usage against. - * @param namespace Namespace the document being used belongs to. - * @param id ID of the document being used. - * @param usageTimestampMillis The timestamp at which the document was used. - * @param systemUsage Whether the usage was reported by a system app against another app's doc. - * @param userHandle Handle of the calling user - * @param callback {@link IAppSearchResultCallback#onResult} will be called with an - * {@link AppSearchResult}<{@link Void}>. - */ - void reportUsage( - in String packageName, - in String databaseName, - in String namespace, - in String id, - in long usageTimestampMillis, - in boolean systemUsage, - in UserHandle userHandle, - in IAppSearchResultCallback callback); - - /** - * Removes documents by ID. - * - * @param packageName The name of the package the document is in. - * @param databaseName The databaseName the document is in. - * @param namespace Namespace of the document to remove. - * @param ids The IDs of the documents to delete - * @param userHandle Handle of the calling user - * @param binderCallStartTimeMillis start timestamp of binder call in Millis - * @param callback - * If the call fails to start, {@link IAppSearchBatchResultCallback#onSystemError} - * will be called with the cause throwable. Otherwise, - * {@link IAppSearchBatchResultCallback#onResult} will be called with an - * {@link AppSearchBatchResult}<{@link String}, {@link Void}> - * where the keys are document IDs. If a document doesn't exist, it will be reported as a - * failure where the {@code throwable} is {@code null}. - */ - void removeByDocumentId( - in String packageName, - in String databaseName, - in String namespace, - in List ids, - in UserHandle userHandle, - in long binderCallStartTimeMillis, - in IAppSearchBatchResultCallback callback); - - /** - * Removes documents by given query. - * - * @param packageName The name of the package to query over. - * @param databaseName The databaseName this query for. - * @param queryExpression String to search for - * @param searchSpecBundle SearchSpec bundle - * @param userHandle Handle of the calling user - * @param binderCallStartTimeMillis start timestamp of binder call in Millis - * @param callback {@link IAppSearchResultCallback#onResult} will be called with an - * {@link AppSearchResult}<{@link Void}>. - */ - void removeByQuery( - in String packageName, - in String databaseName, - in String queryExpression, - in Bundle searchSpecBundle, - in UserHandle userHandle, - in long binderCallStartTimeMillis, - in IAppSearchResultCallback callback); - - /** - * Gets the storage info. - * - * @param packageName The name of the package to get the storage info for. - * @param databaseName The databaseName to get the storage info for. - * @param userHandle Handle of the calling user - * @param callback {@link IAppSearchResultCallback#onResult} will be called with an - * {@link AppSearchResult}<{@link Bundle}>, where the value is a - * {@link StorageInfo}. - */ - void getStorageInfo( - in String packageName, - in String databaseName, - in UserHandle userHandle, - in IAppSearchResultCallback callback); - - /** - * Persists all update/delete requests to the disk. - * - * @param packageName The name of the package to persist to disk for. - * @param userHandle Handle of the calling user - * @param binderCallStartTimeMillis start timestamp of binder call in Millis - */ - void persistToDisk( - in String packageName, - in UserHandle userHandle, - in long binderCallStartTimeMillis); - - /** - * Creates and initializes AppSearchImpl for the calling app. - * - * @param packageName The name of the package to initialize for. - * @param userHandle Handle of the calling user - * @param binderCallStartTimeMillis start timestamp of binder call in Millis - * @param callback {@link IAppSearchResultCallback#onResult} will be called with an - * {@link AppSearchResult}<{@link Void}>. - */ - void initialize( - in String packageName, - in UserHandle userHandle, - in long binderCallStartTimeMillis, - in IAppSearchResultCallback callback); -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchResultCallback.aidl b/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchResultCallback.aidl deleted file mode 100644 index 097f0d1803956..0000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/aidl/IAppSearchResultCallback.aidl +++ /dev/null @@ -1,23 +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.aidl; - -import android.app.appsearch.aidl.AppSearchResultParcel; - -/** {@hide} */ -oneway interface IAppSearchResultCallback { - void onResult(in AppSearchResultParcel resultParcel); -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java deleted file mode 100644 index d493a1c28a3a2..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java +++ /dev/null @@ -1,209 +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 android.annotation.NonNull; -import android.annotation.Nullable; -import android.util.ArrayMap; - -import java.util.Collections; -import java.util.Map; -import java.util.Objects; - -/** - * Provides results for AppSearch batch operations which encompass multiple documents. - * - *

Individual results of a batch operation are separated into two maps: one for successes and one - * for failures. For successes, {@link #getSuccesses()} will return a map of keys to instances of - * the value type. For failures, {@link #getFailures()} will return a map of keys to {@link - * AppSearchResult} objects. - * - *

Alternatively, {@link #getAll()} returns a map of keys to {@link AppSearchResult} objects for - * both successes and failures. - * - * @param The type of the keys for which the results will be reported. - * @param The type of the result objects for successful results. - * @see AppSearchSession#put - * @see AppSearchSession#getByDocumentId - * @see AppSearchSession#remove - */ -public final class AppSearchBatchResult { - @NonNull private final Map mSuccesses; - @NonNull private final Map> mFailures; - @NonNull private final Map> mAll; - - AppSearchBatchResult( - @NonNull Map successes, - @NonNull Map> failures, - @NonNull Map> all) { - mSuccesses = Objects.requireNonNull(successes); - mFailures = Objects.requireNonNull(failures); - mAll = Objects.requireNonNull(all); - } - - /** Returns {@code true} if this {@link AppSearchBatchResult} has no failures. */ - public boolean isSuccess() { - return mFailures.isEmpty(); - } - - /** - * Returns a {@link Map} of keys mapped to instances of the value type for all successful - * individual results. - * - *

Example: {@link AppSearchSession#getByDocumentId} returns an {@link AppSearchBatchResult}. - * Each key (the document ID, of {@code String} type) will map to a {@link GenericDocument} - * object. - * - *

The values of the {@link Map} will not be {@code null}. - */ - @NonNull - public Map getSuccesses() { - return Collections.unmodifiableMap(mSuccesses); - } - - /** - * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all failed - * individual results. - * - *

The values of the {@link Map} will not be {@code null}. - */ - @NonNull - public Map> getFailures() { - return Collections.unmodifiableMap(mFailures); - } - - /** - * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all - * individual results. - * - *

The values of the {@link Map} will not be {@code null}. - */ - @NonNull - public Map> getAll() { - return Collections.unmodifiableMap(mAll); - } - - @Override - @NonNull - public String toString() { - return "{\n successes: " + mSuccesses + "\n failures: " + mFailures + "\n}"; - } - - /** - * Builder for {@link AppSearchBatchResult} objects. - * - * @param The type of the keys for which the results will be reported. - * @param The type of the result objects for successful results. - */ - public static final class Builder { - private ArrayMap mSuccesses = new ArrayMap<>(); - private ArrayMap> mFailures = new ArrayMap<>(); - private ArrayMap> mAll = new ArrayMap<>(); - private boolean mBuilt = false; - - /** - * Associates the {@code key} with the provided successful return value. - * - *

Any previous mapping for a key, whether success or failure, is deleted. - * - *

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 - @NonNull - public Builder setSuccess( - @NonNull KeyType key, @Nullable ValueType value) { - Objects.requireNonNull(key); - resetIfBuilt(); - return setResult(key, AppSearchResult.newSuccessfulResult(value)); - } - - /** - * Associates the {@code key} with the provided failure code and error message. - * - *

Any previous mapping for a key, whether success or failure, is deleted. - * - *

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 - @NonNull - public Builder setFailure( - @NonNull KeyType key, - @AppSearchResult.ResultCode int resultCode, - @Nullable String errorMessage) { - Objects.requireNonNull(key); - resetIfBuilt(); - return setResult(key, AppSearchResult.newFailedResult(resultCode, errorMessage)); - } - - /** - * Associates the {@code key} with the provided {@code result}. - * - *

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 - @NonNull - public Builder setResult( - @NonNull KeyType key, @NonNull AppSearchResult result) { - Objects.requireNonNull(key); - Objects.requireNonNull(result); - resetIfBuilt(); - if (result.isSuccess()) { - mSuccesses.put(key, result.getResultValue()); - mFailures.remove(key); - } else { - mFailures.put(key, result); - mSuccesses.remove(key); - } - mAll.put(key, result); - return this; - } - - /** - * Builds an {@link AppSearchBatchResult} object from the contents of this {@link Builder}. - */ - @NonNull - public AppSearchBatchResult build() { - mBuilt = true; - return new AppSearchBatchResult<>(mSuccesses, mFailures, mAll); - } - - private void resetIfBuilt() { - if (mBuilt) { - mSuccesses = new ArrayMap<>(mSuccesses); - mFailures = new ArrayMap<>(mFailures); - mAll = new ArrayMap<>(mAll); - mBuilt = false; - } - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java deleted file mode 100644 index b1cb132ee088f..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchResult.java +++ /dev/null @@ -1,249 +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 android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.exceptions.AppSearchException; -import android.util.Log; - -import com.android.internal.util.Preconditions; - -import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * Information about the success or failure of an AppSearch call. - * - * @param The type of result object for successful calls. - */ -public final class AppSearchResult { - private static final String TAG = "AppSearchResult"; - - /** - * Result codes from {@link AppSearchSession} methods. - * - * @hide - */ - @IntDef( - value = { - RESULT_OK, - RESULT_UNKNOWN_ERROR, - RESULT_INTERNAL_ERROR, - RESULT_INVALID_ARGUMENT, - RESULT_IO_ERROR, - RESULT_OUT_OF_SPACE, - RESULT_NOT_FOUND, - RESULT_INVALID_SCHEMA, - RESULT_SECURITY_ERROR, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface ResultCode {} - - /** The call was successful. */ - public static final int RESULT_OK = 0; - - /** An unknown error occurred while processing the call. */ - public static final int RESULT_UNKNOWN_ERROR = 1; - - /** - * An internal error occurred within AppSearch, which the caller cannot address. - * - *

This error may be considered similar to {@link IllegalStateException} - */ - public static final int RESULT_INTERNAL_ERROR = 2; - - /** - * The caller supplied invalid arguments to the call. - * - *

This error may be considered similar to {@link IllegalArgumentException}. - */ - public static final int RESULT_INVALID_ARGUMENT = 3; - - /** - * An issue occurred reading or writing to storage. The call might succeed if repeated. - * - *

This error may be considered similar to {@link java.io.IOException}. - */ - public static final int RESULT_IO_ERROR = 4; - - /** Storage is out of space, and no more space could be reclaimed. */ - public static final int RESULT_OUT_OF_SPACE = 5; - - /** An entity the caller requested to interact with does not exist in the system. */ - public static final int RESULT_NOT_FOUND = 6; - - /** The caller supplied a schema which is invalid or incompatible with the previous schema. */ - public static final int RESULT_INVALID_SCHEMA = 7; - - /** The caller requested an operation it does not have privileges for. */ - public static final int RESULT_SECURITY_ERROR = 8; - - private final @ResultCode int mResultCode; - @Nullable private final ValueType mResultValue; - @Nullable private final String mErrorMessage; - - private AppSearchResult( - @ResultCode int resultCode, - @Nullable ValueType resultValue, - @Nullable String errorMessage) { - mResultCode = resultCode; - mResultValue = resultValue; - mErrorMessage = errorMessage; - } - - /** Returns {@code true} if {@link #getResultCode} equals {@link AppSearchResult#RESULT_OK}. */ - public boolean isSuccess() { - return getResultCode() == RESULT_OK; - } - - /** Returns one of the {@code RESULT} constants defined in {@link AppSearchResult}. */ - public @ResultCode int getResultCode() { - return mResultCode; - } - - /** - * Returns the result value associated with this result, if it was successful. - * - *

See the documentation of the particular {@link AppSearchSession} call producing this - * {@link AppSearchResult} for what is placed in the result value by that call. - * - * @throws IllegalStateException if this {@link AppSearchResult} is not successful. - */ - @Nullable - public ValueType getResultValue() { - if (!isSuccess()) { - throw new IllegalStateException("AppSearchResult is a failure: " + this); - } - return mResultValue; - } - - /** - * Returns the error message associated with this result. - * - *

If {@link #isSuccess} is {@code true}, the error message is always {@code null}. The error - * message may be {@code null} even if {@link #isSuccess} is {@code false}. See the - * documentation of the particular {@link AppSearchSession} call producing this {@link - * AppSearchResult} for what is returned by {@link #getErrorMessage}. - */ - @Nullable - public String getErrorMessage() { - return mErrorMessage; - } - - @Override - public boolean equals(@Nullable Object other) { - if (this == other) { - return true; - } - if (!(other instanceof AppSearchResult)) { - return false; - } - AppSearchResult otherResult = (AppSearchResult) other; - return mResultCode == otherResult.mResultCode - && Objects.equals(mResultValue, otherResult.mResultValue) - && Objects.equals(mErrorMessage, otherResult.mErrorMessage); - } - - @Override - public int hashCode() { - return Objects.hash(mResultCode, mResultValue, mErrorMessage); - } - - @Override - @NonNull - public String toString() { - if (isSuccess()) { - return "[SUCCESS]: " + mResultValue; - } - return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage; - } - - /** - * Creates a new successful {@link AppSearchResult}. - * - * @param value An optional value to associate with the successful result of the operation being - * performed. - */ - @NonNull - public static AppSearchResult newSuccessfulResult( - @Nullable ValueType value) { - return new AppSearchResult<>(RESULT_OK, value, /*errorMessage=*/ null); - } - - /** - * 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 - public static AppSearchResult newFailedResult( - @ResultCode int resultCode, @Nullable String errorMessage) { - return new AppSearchResult<>(resultCode, /*resultValue=*/ null, errorMessage); - } - - /** - * Creates a new failed {@link AppSearchResult} by a AppSearchResult in another type. - * - * @hide - */ - @NonNull - public static AppSearchResult newFailedResult( - @NonNull AppSearchResult otherFailedResult) { - Preconditions.checkState( - !otherFailedResult.isSuccess(), - "Cannot convert a success result to a failed result"); - return AppSearchResult.newFailedResult( - otherFailedResult.getResultCode(), otherFailedResult.getErrorMessage()); - } - - /** @hide */ - @NonNull - public static AppSearchResult throwableToFailedResult( - @NonNull Throwable t) { - // Log for traceability. NOT_FOUND is logged at VERBOSE because this error can occur during - // the regular operation of the system (b/183550974). Everything else is logged at DEBUG. - if (t instanceof AppSearchException - && ((AppSearchException) t).getResultCode() == RESULT_NOT_FOUND) { - Log.v(TAG, "Converting throwable to failed result: " + t); - } else { - Log.d(TAG, "Converting throwable to failed result.", t); - } - - if (t instanceof AppSearchException) { - return ((AppSearchException) t).toAppSearchResult(); - } - - String exceptionClass = t.getClass().getSimpleName(); - @AppSearchResult.ResultCode int resultCode; - if (t instanceof IllegalStateException || t instanceof NullPointerException) { - resultCode = AppSearchResult.RESULT_INTERNAL_ERROR; - } else if (t instanceof IllegalArgumentException) { - resultCode = AppSearchResult.RESULT_INVALID_ARGUMENT; - } else if (t instanceof IOException) { - resultCode = AppSearchResult.RESULT_IO_ERROR; - } else if (t instanceof SecurityException) { - resultCode = AppSearchResult.RESULT_SECURITY_ERROR; - } else { - resultCode = AppSearchResult.RESULT_UNKNOWN_ERROR; - } - return AppSearchResult.newFailedResult(resultCode, exceptionClass + ": " + t.getMessage()); - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java b/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java deleted file mode 100644 index 2e04d71e56b2a..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchSchema.java +++ /dev/null @@ -1,922 +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 android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.exceptions.IllegalSchemaException; -import android.app.appsearch.util.BundleUtil; -import android.app.appsearch.util.IndentingStringBuilder; -import android.os.Bundle; -import android.util.ArraySet; - -import com.android.internal.util.Preconditions; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -/** - * The AppSearch Schema for a particular type of document. - * - *

For example, an e-mail message or a music recording could be a schema type. - * - *

The schema consists of type information, properties, and config (like tokenization type). - * - * @see AppSearchSession#setSchema - */ -public final class AppSearchSchema { - private static final String SCHEMA_TYPE_FIELD = "schemaType"; - private static final String PROPERTIES_FIELD = "properties"; - - private final Bundle mBundle; - - /** @hide */ - public AppSearchSchema(@NonNull Bundle bundle) { - Objects.requireNonNull(bundle); - mBundle = bundle; - } - - /** - * Returns the {@link Bundle} populated by this builder. - * - * @hide - */ - @NonNull - public Bundle getBundle() { - return mBundle; - } - - @Override - @NonNull - public String toString() { - IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); - appendAppSearchSchemaString(stringBuilder); - return stringBuilder.toString(); - } - - /** - * Appends a debugging string for the {@link AppSearchSchema} instance to the given string - * builder. - * - * @param builder the builder to append to. - */ - private void appendAppSearchSchemaString(@NonNull IndentingStringBuilder builder) { - Objects.requireNonNull(builder); - - builder.append("{\n"); - builder.increaseIndentLevel(); - builder.append("schemaType: \"").append(getSchemaType()).append("\",\n"); - builder.append("properties: [\n"); - - AppSearchSchema.PropertyConfig[] sortedProperties = - getProperties().toArray(new AppSearchSchema.PropertyConfig[0]); - Arrays.sort(sortedProperties, (o1, o2) -> o1.getName().compareTo(o2.getName())); - - for (int i = 0; i < sortedProperties.length; i++) { - AppSearchSchema.PropertyConfig propertyConfig = sortedProperties[i]; - builder.increaseIndentLevel(); - propertyConfig.appendPropertyConfigString(builder); - if (i != sortedProperties.length - 1) { - builder.append(",\n"); - } - builder.decreaseIndentLevel(); - } - - builder.append("\n"); - builder.append("]\n"); - builder.decreaseIndentLevel(); - builder.append("}"); - } - - /** Returns the name of this schema type, e.g. Email. */ - @NonNull - public String getSchemaType() { - return mBundle.getString(SCHEMA_TYPE_FIELD, ""); - } - - /** - * Returns the list of {@link PropertyConfig}s that are part of this schema. - * - *

This method creates a new list when called. - */ - @NonNull - @SuppressWarnings("MixedMutabilityReturnType") - public List getProperties() { - ArrayList propertyBundles = - mBundle.getParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD); - if (propertyBundles.isEmpty()) { - return Collections.emptyList(); - } - List ret = new ArrayList<>(propertyBundles.size()); - for (int i = 0; i < propertyBundles.size(); i++) { - ret.add(PropertyConfig.fromBundle(propertyBundles.get(i))); - } - return ret; - } - - @Override - public boolean equals(@Nullable Object other) { - if (this == other) { - return true; - } - if (!(other instanceof AppSearchSchema)) { - return false; - } - AppSearchSchema otherSchema = (AppSearchSchema) other; - if (!getSchemaType().equals(otherSchema.getSchemaType())) { - return false; - } - return getProperties().equals(otherSchema.getProperties()); - } - - @Override - public int hashCode() { - return Objects.hash(getSchemaType(), getProperties()); - } - - /** Builder for {@link AppSearchSchema objects}. */ - public static final class Builder { - private final String mSchemaType; - private ArrayList mPropertyBundles = new ArrayList<>(); - private final Set mPropertyNames = new ArraySet<>(); - private boolean mBuilt = false; - - /** Creates a new {@link AppSearchSchema.Builder}. */ - public Builder(@NonNull String schemaType) { - Objects.requireNonNull(schemaType); - mSchemaType = schemaType; - } - - /** Adds a property to the given type. */ - @NonNull - public AppSearchSchema.Builder addProperty(@NonNull PropertyConfig propertyConfig) { - Objects.requireNonNull(propertyConfig); - resetIfBuilt(); - String name = propertyConfig.getName(); - if (!mPropertyNames.add(name)) { - throw new IllegalSchemaException("Property defined more than once: " + name); - } - mPropertyBundles.add(propertyConfig.mBundle); - return this; - } - - /** Constructs a new {@link AppSearchSchema} from the contents of this builder. */ - @NonNull - public AppSearchSchema build() { - Bundle bundle = new Bundle(); - bundle.putString(AppSearchSchema.SCHEMA_TYPE_FIELD, mSchemaType); - bundle.putParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD, mPropertyBundles); - mBuilt = true; - return new AppSearchSchema(bundle); - } - - private void resetIfBuilt() { - if (mBuilt) { - mPropertyBundles = new ArrayList<>(mPropertyBundles); - mBuilt = false; - } - } - } - - /** - * Common configuration for a single property (field) in a Document. - * - *

For example, an {@code EmailMessage} would be a type and the {@code subject} would be a - * property. - */ - public abstract static class PropertyConfig { - static final String NAME_FIELD = "name"; - static final String DATA_TYPE_FIELD = "dataType"; - static final String CARDINALITY_FIELD = "cardinality"; - - /** - * Physical data-types of the contents of the property. - * - * @hide - */ - // NOTE: The integer values of these constants must match the proto enum constants in - // com.google.android.icing.proto.PropertyConfigProto.DataType.Code. - @IntDef( - value = { - DATA_TYPE_STRING, - DATA_TYPE_LONG, - DATA_TYPE_DOUBLE, - DATA_TYPE_BOOLEAN, - DATA_TYPE_BYTES, - DATA_TYPE_DOCUMENT, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DataType {} - - /** @hide */ - public static final int DATA_TYPE_STRING = 1; - - /** @hide */ - public static final int DATA_TYPE_LONG = 2; - - /** @hide */ - public static final int DATA_TYPE_DOUBLE = 3; - - /** @hide */ - public static final int DATA_TYPE_BOOLEAN = 4; - - /** - * Unstructured BLOB. - * - * @hide - */ - public static final int DATA_TYPE_BYTES = 5; - - /** - * Indicates that the property is itself a {@link GenericDocument}, making it part of a - * hierarchical schema. Any property using this DataType MUST have a valid {@link - * PropertyConfig#getSchemaType}. - * - * @hide - */ - public static final int DATA_TYPE_DOCUMENT = 6; - - /** - * The cardinality of the property (whether it is required, optional or repeated). - * - * @hide - */ - // NOTE: The integer values of these constants must match the proto enum constants in - // com.google.android.icing.proto.PropertyConfigProto.Cardinality.Code. - @IntDef( - value = { - CARDINALITY_REPEATED, - CARDINALITY_OPTIONAL, - CARDINALITY_REQUIRED, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface Cardinality {} - - /** Any number of items (including zero) [0...*]. */ - public static final int CARDINALITY_REPEATED = 1; - - /** Zero or one value [0,1]. */ - public static final int CARDINALITY_OPTIONAL = 2; - - /** Exactly one value [1]. */ - public static final int CARDINALITY_REQUIRED = 3; - - final Bundle mBundle; - - @Nullable private Integer mHashCode; - - PropertyConfig(@NonNull Bundle bundle) { - mBundle = Objects.requireNonNull(bundle); - } - - @Override - @NonNull - public String toString() { - IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); - appendPropertyConfigString(stringBuilder); - return stringBuilder.toString(); - } - - /** - * Appends a debug string for the {@link AppSearchSchema.PropertyConfig} instance to the - * given string builder. - * - * @param builder the builder to append to. - */ - void appendPropertyConfigString(@NonNull IndentingStringBuilder builder) { - Objects.requireNonNull(builder); - - builder.append("{\n"); - builder.increaseIndentLevel(); - builder.append("name: \"").append(getName()).append("\",\n"); - - if (this instanceof AppSearchSchema.StringPropertyConfig) { - ((StringPropertyConfig) this).appendStringPropertyConfigFields(builder); - } else if (this instanceof AppSearchSchema.DocumentPropertyConfig) { - ((DocumentPropertyConfig) this).appendDocumentPropertyConfigFields(builder); - } - - switch (getCardinality()) { - case AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED: - builder.append("cardinality: CARDINALITY_REPEATED,\n"); - break; - case AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL: - builder.append("cardinality: CARDINALITY_OPTIONAL,\n"); - break; - case AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED: - builder.append("cardinality: CARDINALITY_REQUIRED,\n"); - break; - default: - builder.append("cardinality: CARDINALITY_UNKNOWN,\n"); - } - - switch (getDataType()) { - case AppSearchSchema.PropertyConfig.DATA_TYPE_STRING: - builder.append("dataType: DATA_TYPE_STRING,\n"); - break; - case AppSearchSchema.PropertyConfig.DATA_TYPE_LONG: - builder.append("dataType: DATA_TYPE_LONG,\n"); - break; - case AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE: - builder.append("dataType: DATA_TYPE_DOUBLE,\n"); - break; - case AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN: - builder.append("dataType: DATA_TYPE_BOOLEAN,\n"); - break; - case AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES: - builder.append("dataType: DATA_TYPE_BYTES,\n"); - break; - case AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT: - builder.append("dataType: DATA_TYPE_DOCUMENT,\n"); - break; - default: - builder.append("dataType: DATA_TYPE_UNKNOWN,\n"); - } - builder.decreaseIndentLevel(); - builder.append("}"); - } - - /** Returns the name of this property. */ - @NonNull - public String getName() { - return mBundle.getString(NAME_FIELD, ""); - } - - /** - * Returns the type of data the property contains (e.g. string, int, bytes, etc). - * - * @hide - */ - public @DataType int getDataType() { - return mBundle.getInt(DATA_TYPE_FIELD, -1); - } - - /** - * Returns the cardinality of the property (whether it is optional, required or repeated). - */ - public @Cardinality int getCardinality() { - return mBundle.getInt(CARDINALITY_FIELD, CARDINALITY_OPTIONAL); - } - - @Override - public boolean equals(@Nullable Object other) { - if (this == other) { - return true; - } - if (!(other instanceof PropertyConfig)) { - return false; - } - PropertyConfig otherProperty = (PropertyConfig) other; - return BundleUtil.deepEquals(this.mBundle, otherProperty.mBundle); - } - - @Override - public int hashCode() { - if (mHashCode == null) { - mHashCode = BundleUtil.deepHashCode(mBundle); - } - return mHashCode; - } - - /** - * Converts a {@link Bundle} into a {@link PropertyConfig} depending on its internal data - * type. - * - *

The bundle is not cloned. - * - * @throws IllegalArgumentException if the bundle does no contain a recognized value in its - * {@code DATA_TYPE_FIELD}. - * @hide - */ - @NonNull - public static PropertyConfig fromBundle(@NonNull Bundle propertyBundle) { - switch (propertyBundle.getInt(PropertyConfig.DATA_TYPE_FIELD)) { - case PropertyConfig.DATA_TYPE_STRING: - return new StringPropertyConfig(propertyBundle); - case PropertyConfig.DATA_TYPE_LONG: - return new LongPropertyConfig(propertyBundle); - case PropertyConfig.DATA_TYPE_DOUBLE: - return new DoublePropertyConfig(propertyBundle); - case PropertyConfig.DATA_TYPE_BOOLEAN: - return new BooleanPropertyConfig(propertyBundle); - case PropertyConfig.DATA_TYPE_BYTES: - return new BytesPropertyConfig(propertyBundle); - case PropertyConfig.DATA_TYPE_DOCUMENT: - return new DocumentPropertyConfig(propertyBundle); - default: - throw new IllegalArgumentException( - "Unsupported property bundle of type " - + propertyBundle.getInt(PropertyConfig.DATA_TYPE_FIELD) - + "; contents: " - + propertyBundle); - } - } - } - - /** Configuration for a property of type String in a Document. */ - public static final class StringPropertyConfig extends PropertyConfig { - private static final String INDEXING_TYPE_FIELD = "indexingType"; - private static final String TOKENIZER_TYPE_FIELD = "tokenizerType"; - - /** - * Encapsulates the configurations on how AppSearch should query/index these terms. - * - * @hide - */ - @IntDef( - value = { - INDEXING_TYPE_NONE, - INDEXING_TYPE_EXACT_TERMS, - INDEXING_TYPE_PREFIXES, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface IndexingType {} - - /** Content in this property will not be tokenized or indexed. */ - public static final int INDEXING_TYPE_NONE = 0; - - /** - * Content in this property should only be returned for queries matching the exact tokens - * appearing in this property. - * - *

Ex. A property with "fool" should NOT match a query for "foo". - */ - public static final int INDEXING_TYPE_EXACT_TERMS = 1; - - /** - * Content in this property should be returned for queries that are either exact matches or - * query matches of the tokens appearing in this property. - * - *

Ex. A property with "fool" should match a query for "foo". - */ - public static final int INDEXING_TYPE_PREFIXES = 2; - - /** - * Configures how tokens should be extracted from this property. - * - * @hide - */ - // NOTE: The integer values of these constants must match the proto enum constants in - // com.google.android.icing.proto.IndexingConfig.TokenizerType.Code. - @IntDef( - value = { - TOKENIZER_TYPE_NONE, - TOKENIZER_TYPE_PLAIN, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface TokenizerType {} - - /** - * This value indicates that no tokens should be extracted from this property. - * - *

It is only valid for tokenizer_type to be 'NONE' if {@link #getIndexingType} is {@link - * #INDEXING_TYPE_NONE}. - */ - public static final int TOKENIZER_TYPE_NONE = 0; - - /** - * Tokenization for plain text. This value indicates that tokens should be extracted from - * this property based on word breaks. Segments of whitespace and punctuation are not - * considered tokens. - * - *

Ex. A property with "foo bar. baz." will produce tokens for "foo", "bar" and "baz". - * The segments " " and "." will not be considered tokens. - * - *

It is only valid for tokenizer_type to be 'PLAIN' if {@link #getIndexingType} is - * {@link #INDEXING_TYPE_EXACT_TERMS} or {@link #INDEXING_TYPE_PREFIXES}. - */ - public static final int TOKENIZER_TYPE_PLAIN = 1; - - StringPropertyConfig(@NonNull Bundle bundle) { - super(bundle); - } - - /** Returns how the property is indexed. */ - public @IndexingType int getIndexingType() { - return mBundle.getInt(INDEXING_TYPE_FIELD); - } - - /** Returns how this property is tokenized (split into words). */ - public @TokenizerType int getTokenizerType() { - return mBundle.getInt(TOKENIZER_TYPE_FIELD); - } - - /** Builder for {@link StringPropertyConfig}. */ - public static final class Builder { - private final String mPropertyName; - private @Cardinality int mCardinality = CARDINALITY_OPTIONAL; - private @IndexingType int mIndexingType = INDEXING_TYPE_NONE; - private @TokenizerType int mTokenizerType = TOKENIZER_TYPE_NONE; - - /** Creates a new {@link StringPropertyConfig.Builder}. */ - public Builder(@NonNull String propertyName) { - mPropertyName = Objects.requireNonNull(propertyName); - } - - /** - * The cardinality of the property (whether it is optional, required or repeated). - * - *

If this method is not called, the default cardinality is {@link - * PropertyConfig#CARDINALITY_OPTIONAL}. - */ - @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass - @NonNull - public StringPropertyConfig.Builder setCardinality(@Cardinality int cardinality) { - Preconditions.checkArgumentInRange( - cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); - mCardinality = cardinality; - return this; - } - - /** - * Configures how a property should be indexed so that it can be retrieved by queries. - * - *

If this method is not called, the default indexing type is {@link - * StringPropertyConfig#INDEXING_TYPE_NONE}, so that it cannot be matched by queries. - */ - @NonNull - public StringPropertyConfig.Builder setIndexingType(@IndexingType int indexingType) { - Preconditions.checkArgumentInRange( - indexingType, INDEXING_TYPE_NONE, INDEXING_TYPE_PREFIXES, "indexingType"); - mIndexingType = indexingType; - return this; - } - - /** - * Configures how this property should be tokenized (split into words). - * - *

If this method is not called, the default indexing type is {@link - * StringPropertyConfig#TOKENIZER_TYPE_NONE}, so that it is not tokenized. - * - *

This method must be called with a value other than {@link - * StringPropertyConfig#TOKENIZER_TYPE_NONE} if the property is indexed (i.e. if {@link - * #setIndexingType} has been called with a value other than {@link - * StringPropertyConfig#INDEXING_TYPE_NONE}). - */ - @NonNull - public StringPropertyConfig.Builder setTokenizerType(@TokenizerType int tokenizerType) { - Preconditions.checkArgumentInRange( - tokenizerType, TOKENIZER_TYPE_NONE, TOKENIZER_TYPE_PLAIN, "tokenizerType"); - mTokenizerType = tokenizerType; - return this; - } - - /** Constructs a new {@link StringPropertyConfig} from the contents of this builder. */ - @NonNull - public StringPropertyConfig build() { - if (mTokenizerType == TOKENIZER_TYPE_NONE) { - Preconditions.checkState( - mIndexingType == INDEXING_TYPE_NONE, - "Cannot set " - + "TOKENIZER_TYPE_NONE with an indexing type other than " - + "INDEXING_TYPE_NONE."); - } else { - Preconditions.checkState( - mIndexingType != INDEXING_TYPE_NONE, - "Cannot set " + "TOKENIZER_TYPE_PLAIN with INDEXING_TYPE_NONE."); - } - Bundle bundle = new Bundle(); - bundle.putString(NAME_FIELD, mPropertyName); - bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_STRING); - bundle.putInt(CARDINALITY_FIELD, mCardinality); - bundle.putInt(INDEXING_TYPE_FIELD, mIndexingType); - bundle.putInt(TOKENIZER_TYPE_FIELD, mTokenizerType); - return new StringPropertyConfig(bundle); - } - } - - /** - * Appends a debug string for the {@link StringPropertyConfig} instance to the given string - * builder. - * - *

This appends fields specific to a {@link StringPropertyConfig} instance. - * - * @param builder the builder to append to. - */ - void appendStringPropertyConfigFields(@NonNull IndentingStringBuilder builder) { - switch (getIndexingType()) { - case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE: - builder.append("indexingType: INDEXING_TYPE_NONE,\n"); - break; - case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS: - builder.append("indexingType: INDEXING_TYPE_EXACT_TERMS,\n"); - break; - case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES: - builder.append("indexingType: INDEXING_TYPE_PREFIXES,\n"); - break; - default: - builder.append("indexingType: INDEXING_TYPE_UNKNOWN,\n"); - } - - switch (getTokenizerType()) { - case AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_NONE: - builder.append("tokenizerType: TOKENIZER_TYPE_NONE,\n"); - break; - case AppSearchSchema.StringPropertyConfig.TOKENIZER_TYPE_PLAIN: - builder.append("tokenizerType: TOKENIZER_TYPE_PLAIN,\n"); - break; - default: - builder.append("tokenizerType: TOKENIZER_TYPE_UNKNOWN,\n"); - } - } - } - - /** Configuration for a property containing a 64-bit integer. */ - public static final class LongPropertyConfig extends PropertyConfig { - LongPropertyConfig(@NonNull Bundle bundle) { - super(bundle); - } - - /** Builder for {@link LongPropertyConfig}. */ - public static final class Builder { - private final String mPropertyName; - private @Cardinality int mCardinality = CARDINALITY_OPTIONAL; - - /** Creates a new {@link LongPropertyConfig.Builder}. */ - public Builder(@NonNull String propertyName) { - mPropertyName = Objects.requireNonNull(propertyName); - } - - /** - * The cardinality of the property (whether it is optional, required or repeated). - * - *

If this method is not called, the default cardinality is {@link - * PropertyConfig#CARDINALITY_OPTIONAL}. - */ - @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass - @NonNull - public LongPropertyConfig.Builder setCardinality(@Cardinality int cardinality) { - Preconditions.checkArgumentInRange( - cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); - mCardinality = cardinality; - return this; - } - - /** Constructs a new {@link LongPropertyConfig} from the contents of this builder. */ - @NonNull - public LongPropertyConfig build() { - Bundle bundle = new Bundle(); - bundle.putString(NAME_FIELD, mPropertyName); - bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_LONG); - bundle.putInt(CARDINALITY_FIELD, mCardinality); - return new LongPropertyConfig(bundle); - } - } - } - - /** Configuration for a property containing a double-precision decimal number. */ - public static final class DoublePropertyConfig extends PropertyConfig { - DoublePropertyConfig(@NonNull Bundle bundle) { - super(bundle); - } - - /** Builder for {@link DoublePropertyConfig}. */ - public static final class Builder { - private final String mPropertyName; - private @Cardinality int mCardinality = CARDINALITY_OPTIONAL; - - /** Creates a new {@link DoublePropertyConfig.Builder}. */ - public Builder(@NonNull String propertyName) { - mPropertyName = Objects.requireNonNull(propertyName); - } - - /** - * The cardinality of the property (whether it is optional, required or repeated). - * - *

If this method is not called, the default cardinality is {@link - * PropertyConfig#CARDINALITY_OPTIONAL}. - */ - @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass - @NonNull - public DoublePropertyConfig.Builder setCardinality(@Cardinality int cardinality) { - Preconditions.checkArgumentInRange( - cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); - mCardinality = cardinality; - return this; - } - - /** Constructs a new {@link DoublePropertyConfig} from the contents of this builder. */ - @NonNull - public DoublePropertyConfig build() { - Bundle bundle = new Bundle(); - bundle.putString(NAME_FIELD, mPropertyName); - bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_DOUBLE); - bundle.putInt(CARDINALITY_FIELD, mCardinality); - return new DoublePropertyConfig(bundle); - } - } - } - - /** Configuration for a property containing a boolean. */ - public static final class BooleanPropertyConfig extends PropertyConfig { - BooleanPropertyConfig(@NonNull Bundle bundle) { - super(bundle); - } - - /** Builder for {@link BooleanPropertyConfig}. */ - public static final class Builder { - private final String mPropertyName; - private @Cardinality int mCardinality = CARDINALITY_OPTIONAL; - - /** Creates a new {@link BooleanPropertyConfig.Builder}. */ - public Builder(@NonNull String propertyName) { - mPropertyName = Objects.requireNonNull(propertyName); - } - - /** - * The cardinality of the property (whether it is optional, required or repeated). - * - *

If this method is not called, the default cardinality is {@link - * PropertyConfig#CARDINALITY_OPTIONAL}. - */ - @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass - @NonNull - public BooleanPropertyConfig.Builder setCardinality(@Cardinality int cardinality) { - Preconditions.checkArgumentInRange( - cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); - mCardinality = cardinality; - return this; - } - - /** Constructs a new {@link BooleanPropertyConfig} from the contents of this builder. */ - @NonNull - public BooleanPropertyConfig build() { - Bundle bundle = new Bundle(); - bundle.putString(NAME_FIELD, mPropertyName); - bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_BOOLEAN); - bundle.putInt(CARDINALITY_FIELD, mCardinality); - return new BooleanPropertyConfig(bundle); - } - } - } - - /** Configuration for a property containing a byte array. */ - public static final class BytesPropertyConfig extends PropertyConfig { - BytesPropertyConfig(@NonNull Bundle bundle) { - super(bundle); - } - - /** Builder for {@link BytesPropertyConfig}. */ - public static final class Builder { - private final String mPropertyName; - private @Cardinality int mCardinality = CARDINALITY_OPTIONAL; - - /** Creates a new {@link BytesPropertyConfig.Builder}. */ - public Builder(@NonNull String propertyName) { - mPropertyName = Objects.requireNonNull(propertyName); - } - - /** - * The cardinality of the property (whether it is optional, required or repeated). - * - *

If this method is not called, the default cardinality is {@link - * PropertyConfig#CARDINALITY_OPTIONAL}. - */ - @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass - @NonNull - public BytesPropertyConfig.Builder setCardinality(@Cardinality int cardinality) { - Preconditions.checkArgumentInRange( - cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); - mCardinality = cardinality; - return this; - } - - /** Constructs a new {@link BytesPropertyConfig} from the contents of this builder. */ - @NonNull - public BytesPropertyConfig build() { - Bundle bundle = new Bundle(); - bundle.putString(NAME_FIELD, mPropertyName); - bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_BYTES); - bundle.putInt(CARDINALITY_FIELD, mCardinality); - return new BytesPropertyConfig(bundle); - } - } - } - - /** Configuration for a property containing another Document. */ - public static final class DocumentPropertyConfig extends PropertyConfig { - private static final String SCHEMA_TYPE_FIELD = "schemaType"; - private static final String INDEX_NESTED_PROPERTIES_FIELD = "indexNestedProperties"; - - DocumentPropertyConfig(@NonNull Bundle bundle) { - super(bundle); - } - - /** Returns the logical schema-type of the contents of this document property. */ - @NonNull - public String getSchemaType() { - return Objects.requireNonNull(mBundle.getString(SCHEMA_TYPE_FIELD)); - } - - /** - * Returns whether fields in the nested document should be indexed according to that - * document's schema. - * - *

If false, the nested document's properties are not indexed regardless of its own - * schema. - */ - public boolean shouldIndexNestedProperties() { - return mBundle.getBoolean(INDEX_NESTED_PROPERTIES_FIELD); - } - - /** Builder for {@link DocumentPropertyConfig}. */ - public static final class Builder { - private final String mPropertyName; - private final String mSchemaType; - private @Cardinality int mCardinality = CARDINALITY_OPTIONAL; - private boolean mShouldIndexNestedProperties = false; - - /** - * Creates a new {@link DocumentPropertyConfig.Builder}. - * - * @param propertyName The logical name of the property in the schema, which will be - * used as the key for this property in {@link - * GenericDocument.Builder#setPropertyDocument}. - * @param schemaType The type of documents which will be stored in this property. - * Documents of different types cannot be mixed into a single property. - */ - public Builder(@NonNull String propertyName, @NonNull String schemaType) { - mPropertyName = Objects.requireNonNull(propertyName); - mSchemaType = Objects.requireNonNull(schemaType); - } - - /** - * The cardinality of the property (whether it is optional, required or repeated). - * - *

If this method is not called, the default cardinality is {@link - * PropertyConfig#CARDINALITY_OPTIONAL}. - */ - @SuppressWarnings("MissingGetterMatchingBuilder") // getter defined in superclass - @NonNull - public DocumentPropertyConfig.Builder setCardinality(@Cardinality int cardinality) { - Preconditions.checkArgumentInRange( - cardinality, CARDINALITY_REPEATED, CARDINALITY_REQUIRED, "cardinality"); - mCardinality = cardinality; - return this; - } - - /** - * Configures whether fields in the nested document should be indexed according to that - * document's schema. - * - *

If false, the nested document's properties are not indexed regardless of its own - * schema. - */ - @NonNull - public DocumentPropertyConfig.Builder setShouldIndexNestedProperties( - boolean indexNestedProperties) { - mShouldIndexNestedProperties = indexNestedProperties; - return this; - } - - /** Constructs a new {@link PropertyConfig} from the contents of this builder. */ - @NonNull - public DocumentPropertyConfig build() { - Bundle bundle = new Bundle(); - bundle.putString(NAME_FIELD, mPropertyName); - bundle.putInt(DATA_TYPE_FIELD, DATA_TYPE_DOCUMENT); - bundle.putInt(CARDINALITY_FIELD, mCardinality); - bundle.putBoolean(INDEX_NESTED_PROPERTIES_FIELD, mShouldIndexNestedProperties); - bundle.putString(SCHEMA_TYPE_FIELD, mSchemaType); - return new DocumentPropertyConfig(bundle); - } - } - - /** - * Appends a debug string for the {@link DocumentPropertyConfig} instance to the given - * string builder. - * - *

This appends fields specific to a {@link DocumentPropertyConfig} instance. - * - * @param builder the builder to append to. - */ - void appendDocumentPropertyConfigFields(@NonNull IndentingStringBuilder builder) { - builder.append("shouldIndexNestedProperties: ") - .append(shouldIndexNestedProperties()) - .append(",\n"); - - builder.append("schemaType: \"").append(getSchemaType()).append("\",\n"); - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java deleted file mode 100644 index 4d27519562bc9..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GenericDocument.java +++ /dev/null @@ -1,1342 +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 android.annotation.CurrentTimeMillisLong; -import android.annotation.IntRange; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SuppressLint; -import android.app.appsearch.util.BundleUtil; -import android.app.appsearch.util.IndentingStringBuilder; -import android.os.Bundle; -import android.os.Parcelable; -import android.util.Log; - -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -/** - * Represents a document unit. - * - *

Documents contain structured data conforming to their {@link AppSearchSchema} type. Each - * document is uniquely identified by a namespace and a String ID within that namespace. - * - *

Documents are constructed by using the {@link GenericDocument.Builder}. - * - * @see AppSearchSession#put - * @see AppSearchSession#getByDocumentId - * @see AppSearchSession#search - */ -public class GenericDocument { - private static final String TAG = "AppSearchGenericDocumen"; - - /** The maximum number of indexed properties a document can have. */ - private static final int MAX_INDEXED_PROPERTIES = 16; - - /** The default score of document. */ - private static final int DEFAULT_SCORE = 0; - - /** The default time-to-live in millisecond of a document, which is infinity. */ - private static final long DEFAULT_TTL_MILLIS = 0L; - - private static final String PROPERTIES_FIELD = "properties"; - private static final String BYTE_ARRAY_FIELD = "byteArray"; - private static final String SCHEMA_TYPE_FIELD = "schemaType"; - private static final String ID_FIELD = "id"; - private static final String SCORE_FIELD = "score"; - private static final String TTL_MILLIS_FIELD = "ttlMillis"; - private static final String CREATION_TIMESTAMP_MILLIS_FIELD = "creationTimestampMillis"; - private static final String NAMESPACE_FIELD = "namespace"; - - /** - * The maximum number of indexed properties a document can have. - * - *

Indexed properties are properties which are strings where the {@link - * AppSearchSchema.StringPropertyConfig#getIndexingType} value is anything other than {@link - * AppSearchSchema.StringPropertyConfig.IndexingType#INDEXING_TYPE_NONE}. - */ - public static int getMaxIndexedProperties() { - return MAX_INDEXED_PROPERTIES; - } - - /** - * Contains all {@link GenericDocument} information in a packaged format. - * - *

Keys are the {@code *_FIELD} constants in this class. - */ - @NonNull final Bundle mBundle; - - /** Contains all properties in {@link GenericDocument} to support getting properties via name */ - @NonNull private final Bundle mProperties; - - @NonNull private final String mId; - @NonNull private final String mSchemaType; - private final long mCreationTimestampMillis; - @Nullable private Integer mHashCode; - - /** - * Rebuilds a {@link GenericDocument} from a bundle. - * - * @param bundle Packaged {@link GenericDocument} data, such as the result of {@link - * #getBundle}. - * @hide - */ - public GenericDocument(@NonNull Bundle bundle) { - Objects.requireNonNull(bundle); - mBundle = bundle; - mProperties = Objects.requireNonNull(bundle.getParcelable(PROPERTIES_FIELD)); - mId = Objects.requireNonNull(mBundle.getString(ID_FIELD)); - mSchemaType = Objects.requireNonNull(mBundle.getString(SCHEMA_TYPE_FIELD)); - mCreationTimestampMillis = - mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD, System.currentTimeMillis()); - } - - /** - * Creates a new {@link GenericDocument} from an existing instance. - * - *

This method should be only used by constructor of a subclass. - */ - protected GenericDocument(@NonNull GenericDocument document) { - this(document.mBundle); - } - - /** - * Returns the {@link Bundle} populated by this builder. - * - * @hide - */ - @NonNull - public Bundle getBundle() { - return mBundle; - } - - /** Returns the unique identifier of the {@link GenericDocument}. */ - @NonNull - public String getId() { - return mId; - } - - /** Returns the namespace of the {@link GenericDocument}. */ - @NonNull - public String getNamespace() { - return mBundle.getString(NAMESPACE_FIELD, /*defaultValue=*/ ""); - } - - /** Returns the {@link AppSearchSchema} type of the {@link GenericDocument}. */ - @NonNull - public String getSchemaType() { - return mSchemaType; - } - - /** - * Returns the creation timestamp of the {@link GenericDocument}, in milliseconds. - * - *

The value is in the {@link System#currentTimeMillis} time base. - */ - @CurrentTimeMillisLong - public long getCreationTimestampMillis() { - return mCreationTimestampMillis; - } - - /** - * Returns the TTL (time-to-live) of the {@link GenericDocument}, in milliseconds. - * - *

The TTL is measured against {@link #getCreationTimestampMillis}. At the timestamp of - * {@code creationTimestampMillis + ttlMillis}, measured in the {@link System#currentTimeMillis} - * time base, the document will be auto-deleted. - * - *

The default value is 0, which means the document is permanent and won't be auto-deleted - * until the app is uninstalled or {@link AppSearchSession#remove} is called. - */ - public long getTtlMillis() { - return mBundle.getLong(TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS); - } - - /** - * Returns the score of the {@link GenericDocument}. - * - *

The score is a query-independent measure of the document's quality, relative to other - * {@link GenericDocument} objects of the same {@link AppSearchSchema} type. - * - *

Results may be sorted by score using {@link SearchSpec.Builder#setRankingStrategy}. - * Documents with higher scores are considered better than documents with lower scores. - * - *

Any non-negative integer can be used a score. - */ - public int getScore() { - return mBundle.getInt(SCORE_FIELD, DEFAULT_SCORE); - } - - /** Returns the names of all properties defined in this document. */ - @NonNull - public Set getPropertyNames() { - return Collections.unmodifiableSet(mProperties.keySet()); - } - - /** - * Retrieves the property value with the given path as {@link Object}. - * - *

A path can be a simple property name, such as those returned by {@link #getPropertyNames}. - * It may also be a dot-delimited path through the nested document hierarchy, with nested {@link - * GenericDocument} properties accessed via {@code '.'} and repeated properties optionally - * indexed into via {@code [n]}. - * - *

For example, given the following {@link GenericDocument}: - * - *

-     *     (Message) {
-     *         from: "sender@example.com"
-     *         to: [{
-     *             name: "Albert Einstein"
-     *             email: "einstein@example.com"
-     *           }, {
-     *             name: "Marie Curie"
-     *             email: "curie@example.com"
-     *           }]
-     *         tags: ["important", "inbox"]
-     *         subject: "Hello"
-     *     }
-     * 
- * - *

Here are some example paths and their results: - * - *

    - *
  • {@code "from"} returns {@code "sender@example.com"} as a {@link String} array with one - * element - *
  • {@code "to"} returns the two nested documents containing contact information as a - * {@link GenericDocument} array with two elements - *
  • {@code "to[1]"} returns the second nested document containing Marie Curie's contact - * information as a {@link GenericDocument} array with one element - *
  • {@code "to[1].email"} returns {@code "curie@example.com"} - *
  • {@code "to[100].email"} returns {@code null} as this particular document does not have - * that many elements in its {@code "to"} array. - *
  • {@code "to.email"} aggregates emails across all nested documents that have them, - * returning {@code ["einstein@example.com", "curie@example.com"]} as a {@link String} - * array with two elements. - *
- * - *

If you know the expected type of the property you are retrieving, it is recommended to use - * one of the typed versions of this method instead, such as {@link #getPropertyString} or - * {@link #getPropertyStringArray}. - * - * @param path The path to look for. - * @return The entry with the given path as an object or {@code null} if there is no such path. - * The returned object will be one of the following types: {@code String[]}, {@code long[]}, - * {@code double[]}, {@code boolean[]}, {@code byte[][]}, {@code GenericDocument[]}. - */ - @Nullable - public Object getProperty(@NonNull String path) { - Objects.requireNonNull(path); - Object rawValue = getRawPropertyFromRawDocument(path, mBundle); - - // Unpack the raw value into the types the user expects, if required. - if (rawValue instanceof Bundle) { - // getRawPropertyFromRawDocument may return a document as a bare Bundle as a performance - // optimization for lookups. - GenericDocument document = new GenericDocument((Bundle) rawValue); - return new GenericDocument[] {document}; - } - - if (rawValue instanceof List) { - // byte[][] fields are packed into List where each Bundle contains just a single - // entry: BYTE_ARRAY_FIELD -> byte[]. - @SuppressWarnings("unchecked") - List bundles = (List) rawValue; - if (bundles.size() == 0) { - return null; - } - byte[][] bytes = new byte[bundles.size()][]; - for (int i = 0; i < bundles.size(); i++) { - Bundle bundle = bundles.get(i); - if (bundle == null) { - Log.e(TAG, "The inner bundle is null at " + i + ", for path: " + path); - continue; - } - byte[] innerBytes = bundle.getByteArray(BYTE_ARRAY_FIELD); - if (innerBytes == null) { - Log.e(TAG, "The bundle at " + i + " contains a null byte[]."); - continue; - } - bytes[i] = innerBytes; - } - return bytes; - } - - if (rawValue instanceof Parcelable[]) { - // The underlying Bundle of nested GenericDocuments is packed into a Parcelable array. - // We must unpack it into GenericDocument instances. - Parcelable[] bundles = (Parcelable[]) rawValue; - if (bundles.length == 0) { - return null; - } - GenericDocument[] documents = new GenericDocument[bundles.length]; - for (int i = 0; i < bundles.length; i++) { - if (bundles[i] == null) { - Log.e(TAG, "The inner bundle is null at " + i + ", for path: " + path); - continue; - } - if (!(bundles[i] instanceof Bundle)) { - Log.e( - TAG, - "The inner element at " - + i - + " is a " - + bundles[i].getClass() - + ", not a Bundle for path: " - + path); - continue; - } - documents[i] = new GenericDocument((Bundle) bundles[i]); - } - return documents; - } - - // Otherwise the raw property is the same as the final property and needs no transformation. - return rawValue; - } - - /** - * Looks up a property path within the given document bundle. - * - *

The return value may be any of GenericDocument's internal repeated storage types - * (String[], long[], double[], boolean[], ArrayList<Bundle>, Parcelable[]). - */ - @Nullable - private static Object getRawPropertyFromRawDocument( - @NonNull String path, @NonNull Bundle documentBundle) { - Objects.requireNonNull(path); - Objects.requireNonNull(documentBundle); - Bundle properties = Objects.requireNonNull(documentBundle.getBundle(PROPERTIES_FIELD)); - - // Determine whether the path is just a raw property name with no control characters - int controlIdx = -1; - boolean controlIsIndex = false; - for (int i = 0; i < path.length(); i++) { - char c = path.charAt(i); - if (c == '[' || c == '.') { - controlIdx = i; - controlIsIndex = c == '['; - break; - } - } - - // Look up the value of the first path element - Object firstElementValue; - if (controlIdx == -1) { - firstElementValue = properties.get(path); - } else { - String name = path.substring(0, controlIdx); - firstElementValue = properties.get(name); - } - - // If the path has no further elements, we're done. - if (firstElementValue == null || controlIdx == -1) { - return firstElementValue; - } - - // At this point, for a path like "recipients[0]", firstElementValue contains the value of - // "recipients". If the first element of the path is an indexed value, we now update - // firstElementValue to contain "recipients[0]" instead. - String remainingPath; - if (!controlIsIndex) { - // Remaining path is everything after the . - remainingPath = path.substring(controlIdx + 1); - } else { - int endBracketIdx = path.indexOf(']', controlIdx); - if (endBracketIdx == -1) { - throw new IllegalArgumentException("Malformed path (no ending ']'): " + path); - } - if (endBracketIdx + 1 < path.length() && path.charAt(endBracketIdx + 1) != '.') { - throw new IllegalArgumentException( - "Malformed path (']' not followed by '.'): " + path); - } - String indexStr = path.substring(controlIdx + 1, endBracketIdx); - int index = Integer.parseInt(indexStr); - if (index < 0) { - throw new IllegalArgumentException("Path index less than 0: " + path); - } - - // Remaining path is everything after the [n] - if (endBracketIdx + 1 < path.length()) { - // More path remains, and we've already checked that charAt(endBracketIdx+1) == . - remainingPath = path.substring(endBracketIdx + 2); - } else { - // No more path remains. - remainingPath = null; - } - - // Extract the right array element - Object extractedValue = null; - if (firstElementValue instanceof String[]) { - String[] stringValues = (String[]) firstElementValue; - if (index < stringValues.length) { - extractedValue = Arrays.copyOfRange(stringValues, index, index + 1); - } - } else if (firstElementValue instanceof long[]) { - long[] longValues = (long[]) firstElementValue; - if (index < longValues.length) { - extractedValue = Arrays.copyOfRange(longValues, index, index + 1); - } - } else if (firstElementValue instanceof double[]) { - double[] doubleValues = (double[]) firstElementValue; - if (index < doubleValues.length) { - extractedValue = Arrays.copyOfRange(doubleValues, index, index + 1); - } - } else if (firstElementValue instanceof boolean[]) { - boolean[] booleanValues = (boolean[]) firstElementValue; - if (index < booleanValues.length) { - extractedValue = Arrays.copyOfRange(booleanValues, index, index + 1); - } - } else if (firstElementValue instanceof List) { - @SuppressWarnings("unchecked") - List bundles = (List) firstElementValue; - if (index < bundles.size()) { - extractedValue = bundles.subList(index, index + 1); - } - } else if (firstElementValue instanceof Parcelable[]) { - // Special optimization: to avoid creating new singleton arrays for traversing paths - // we return the bare document Bundle in this particular case. - Parcelable[] bundles = (Parcelable[]) firstElementValue; - if (index < bundles.length) { - extractedValue = (Bundle) bundles[index]; - } - } else { - throw new IllegalStateException("Unsupported value type: " + firstElementValue); - } - firstElementValue = extractedValue; - } - - // If we are at the end of the path or there are no deeper elements in this document, we - // have nothing to recurse into. - if (firstElementValue == null || remainingPath == null) { - return firstElementValue; - } - - // More of the path remains; recursively evaluate it - if (firstElementValue instanceof Bundle) { - return getRawPropertyFromRawDocument(remainingPath, (Bundle) firstElementValue); - } else if (firstElementValue instanceof Parcelable[]) { - Parcelable[] parcelables = (Parcelable[]) firstElementValue; - if (parcelables.length == 1) { - return getRawPropertyFromRawDocument(remainingPath, (Bundle) parcelables[0]); - } - - // Slowest path: we're collecting values across repeated nested docs. (Example: given a - // path like recipient.name, where recipient is a repeated field, we return a string - // array where each recipient's name is an array element). - // - // Performance note: Suppose that we have a property path "a.b.c" where the "a" - // property has N document values and each containing a "b" property with M document - // values and each of those containing a "c" property with an int array. - // - // We'll allocate a new ArrayList for each of the "b" properties, add the M int arrays - // from the "c" properties to it and then we'll allocate an int array in - // flattenAccumulator before returning that (1 + M allocation per "b" property). - // - // When we're on the "a" properties, we'll allocate an ArrayList and add the N - // flattened int arrays returned from the "b" properties to the list. Then we'll - // allocate an int array in flattenAccumulator (1 + N ("b" allocs) allocations per "a"). - // So this implementation could incur 1 + N + NM allocs. - // - // However, we expect the vast majority of getProperty calls to be either for direct - // property names (not paths) or else property paths returned from snippetting, which - // always refer to exactly one property value and don't aggregate across repeated - // values. The implementation is optimized for these two cases, requiring no additional - // allocations. So we've decided that the above performance characteristics are OK for - // the less used path. - List accumulator = new ArrayList<>(parcelables.length); - for (int i = 0; i < parcelables.length; i++) { - Object value = - getRawPropertyFromRawDocument(remainingPath, (Bundle) parcelables[i]); - if (value != null) { - accumulator.add(value); - } - } - return flattenAccumulator(accumulator); - } else { - Log.e(TAG, "Failed to apply path to document; no nested value found: " + path); - return null; - } - } - - /** - * Combines accumulated repeated properties from multiple documents into a single array. - * - * @param accumulator List containing objects of the following types: {@code String[]}, {@code - * long[]}, {@code double[]}, {@code boolean[]}, {@code List}, or {@code - * Parcelable[]}. - * @return The result of concatenating each individual list element into a larger array/list of - * the same type. - */ - @Nullable - private static Object flattenAccumulator(@NonNull List accumulator) { - if (accumulator.isEmpty()) { - return null; - } - Object first = accumulator.get(0); - if (first instanceof String[]) { - int length = 0; - for (int i = 0; i < accumulator.size(); i++) { - length += ((String[]) accumulator.get(i)).length; - } - String[] result = new String[length]; - int total = 0; - for (int i = 0; i < accumulator.size(); i++) { - String[] castValue = (String[]) accumulator.get(i); - System.arraycopy(castValue, 0, result, total, castValue.length); - total += castValue.length; - } - return result; - } - if (first instanceof long[]) { - int length = 0; - for (int i = 0; i < accumulator.size(); i++) { - length += ((long[]) accumulator.get(i)).length; - } - long[] result = new long[length]; - int total = 0; - for (int i = 0; i < accumulator.size(); i++) { - long[] castValue = (long[]) accumulator.get(i); - System.arraycopy(castValue, 0, result, total, castValue.length); - total += castValue.length; - } - return result; - } - if (first instanceof double[]) { - int length = 0; - for (int i = 0; i < accumulator.size(); i++) { - length += ((double[]) accumulator.get(i)).length; - } - double[] result = new double[length]; - int total = 0; - for (int i = 0; i < accumulator.size(); i++) { - double[] castValue = (double[]) accumulator.get(i); - System.arraycopy(castValue, 0, result, total, castValue.length); - total += castValue.length; - } - return result; - } - if (first instanceof boolean[]) { - int length = 0; - for (int i = 0; i < accumulator.size(); i++) { - length += ((boolean[]) accumulator.get(i)).length; - } - boolean[] result = new boolean[length]; - int total = 0; - for (int i = 0; i < accumulator.size(); i++) { - boolean[] castValue = (boolean[]) accumulator.get(i); - System.arraycopy(castValue, 0, result, total, castValue.length); - total += castValue.length; - } - return result; - } - if (first instanceof List) { - int length = 0; - for (int i = 0; i < accumulator.size(); i++) { - length += ((List) accumulator.get(i)).size(); - } - List result = new ArrayList<>(length); - for (int i = 0; i < accumulator.size(); i++) { - @SuppressWarnings("unchecked") - List castValue = (List) accumulator.get(i); - result.addAll(castValue); - } - return result; - } - if (first instanceof Parcelable[]) { - int length = 0; - for (int i = 0; i < accumulator.size(); i++) { - length += ((Parcelable[]) accumulator.get(i)).length; - } - Parcelable[] result = new Parcelable[length]; - int total = 0; - for (int i = 0; i < accumulator.size(); i++) { - Parcelable[] castValue = (Parcelable[]) accumulator.get(i); - System.arraycopy(castValue, 0, result, total, castValue.length); - total += castValue.length; - } - return result; - } - throw new IllegalStateException("Unexpected property type: " + first); - } - - /** - * Retrieves a {@link String} property by path. - * - *

See {@link #getProperty} for a detailed description of the path syntax. - * - * @param path The path to look for. - * @return The first {@link String} associated with the given path or {@code null} if there is - * no such value or the value is of a different type. - */ - @Nullable - public String getPropertyString(@NonNull String path) { - Objects.requireNonNull(path); - String[] propertyArray = getPropertyStringArray(path); - if (propertyArray == null || propertyArray.length == 0) { - return null; - } - warnIfSinglePropertyTooLong("String", path, propertyArray.length); - return propertyArray[0]; - } - - /** - * Retrieves a {@code long} property by path. - * - *

See {@link #getProperty} for a detailed description of the path syntax. - * - * @param path The path to look for. - * @return The first {@code long} associated with the given path or default value {@code 0} if - * there is no such value or the value is of a different type. - */ - public long getPropertyLong(@NonNull String path) { - Objects.requireNonNull(path); - long[] propertyArray = getPropertyLongArray(path); - if (propertyArray == null || propertyArray.length == 0) { - return 0; - } - warnIfSinglePropertyTooLong("Long", path, propertyArray.length); - return propertyArray[0]; - } - - /** - * Retrieves a {@code double} property by path. - * - *

See {@link #getProperty} for a detailed description of the path syntax. - * - * @param path The path to look for. - * @return The first {@code double} associated with the given path or default value {@code 0.0} - * if there is no such value or the value is of a different type. - */ - public double getPropertyDouble(@NonNull String path) { - Objects.requireNonNull(path); - double[] propertyArray = getPropertyDoubleArray(path); - if (propertyArray == null || propertyArray.length == 0) { - return 0.0; - } - warnIfSinglePropertyTooLong("Double", path, propertyArray.length); - return propertyArray[0]; - } - - /** - * Retrieves a {@code boolean} property by path. - * - *

See {@link #getProperty} for a detailed description of the path syntax. - * - * @param path The path to look for. - * @return The first {@code boolean} associated with the given path or default value {@code - * false} if there is no such value or the value is of a different type. - */ - public boolean getPropertyBoolean(@NonNull String path) { - Objects.requireNonNull(path); - boolean[] propertyArray = getPropertyBooleanArray(path); - if (propertyArray == null || propertyArray.length == 0) { - return false; - } - warnIfSinglePropertyTooLong("Boolean", path, propertyArray.length); - return propertyArray[0]; - } - - /** - * Retrieves a {@code byte[]} property by path. - * - *

See {@link #getProperty} for a detailed description of the path syntax. - * - * @param path The path to look for. - * @return The first {@code byte[]} associated with the given path or {@code null} if there is - * no such value or the value is of a different type. - */ - @Nullable - public byte[] getPropertyBytes(@NonNull String path) { - Objects.requireNonNull(path); - byte[][] propertyArray = getPropertyBytesArray(path); - if (propertyArray == null || propertyArray.length == 0) { - return null; - } - warnIfSinglePropertyTooLong("ByteArray", path, propertyArray.length); - return propertyArray[0]; - } - - /** - * Retrieves a {@link GenericDocument} property by path. - * - *

See {@link #getProperty} for a detailed description of the path syntax. - * - * @param path The path to look for. - * @return The first {@link GenericDocument} associated with the given path or {@code null} if - * there is no such value or the value is of a different type. - */ - @Nullable - public GenericDocument getPropertyDocument(@NonNull String path) { - Objects.requireNonNull(path); - GenericDocument[] propertyArray = getPropertyDocumentArray(path); - if (propertyArray == null || propertyArray.length == 0) { - return null; - } - warnIfSinglePropertyTooLong("Document", path, propertyArray.length); - return propertyArray[0]; - } - - /** Prints a warning to logcat if the given propertyLength is greater than 1. */ - private static void warnIfSinglePropertyTooLong( - @NonNull String propertyType, @NonNull String path, int propertyLength) { - if (propertyLength > 1) { - Log.w( - TAG, - "The value for \"" - + path - + "\" contains " - + propertyLength - + " elements. Only the first one will be returned from " - + "getProperty" - + propertyType - + "(). Try getProperty" - + propertyType - + "Array()."); - } - } - - /** - * Retrieves a repeated {@code String} property by path. - * - *

See {@link #getProperty} for a detailed description of the path syntax. - * - * @param path The path to look for. - * @return The {@code String[]} associated with the given path, or {@code null} if no value is - * set or the value is of a different type. - */ - @Nullable - public String[] getPropertyStringArray(@NonNull String path) { - Objects.requireNonNull(path); - Object value = getProperty(path); - return safeCastProperty(path, value, String[].class); - } - - /** - * Retrieves a repeated {@code long[]} property by path. - * - *

See {@link #getProperty} for a detailed description of the path syntax. - * - * @param path The path to look for. - * @return The {@code long[]} associated with the given path, or {@code null} if no value is set - * or the value is of a different type. - */ - @Nullable - public long[] getPropertyLongArray(@NonNull String path) { - Objects.requireNonNull(path); - Object value = getProperty(path); - return safeCastProperty(path, value, long[].class); - } - - /** - * Retrieves a repeated {@code double} property by path. - * - *

See {@link #getProperty} for a detailed description of the path syntax. - * - * @param path The path to look for. - * @return The {@code double[]} associated with the given path, or {@code null} if no value is - * set or the value is of a different type. - */ - @Nullable - public double[] getPropertyDoubleArray(@NonNull String path) { - Objects.requireNonNull(path); - Object value = getProperty(path); - return safeCastProperty(path, value, double[].class); - } - - /** - * Retrieves a repeated {@code boolean} property by path. - * - *

See {@link #getProperty} for a detailed description of the path syntax. - * - * @param path The path to look for. - * @return The {@code boolean[]} associated with the given path, or {@code null} if no value is - * set or the value is of a different type. - */ - @Nullable - public boolean[] getPropertyBooleanArray(@NonNull String path) { - Objects.requireNonNull(path); - Object value = getProperty(path); - return safeCastProperty(path, value, boolean[].class); - } - - /** - * Retrieves a {@code byte[][]} property by path. - * - *

See {@link #getProperty} for a detailed description of the path syntax. - * - * @param path The path to look for. - * @return The {@code byte[][]} associated with the given path, or {@code null} if no value is - * set or the value is of a different type. - */ - @SuppressLint("ArrayReturn") - @Nullable - public byte[][] getPropertyBytesArray(@NonNull String path) { - Objects.requireNonNull(path); - Object value = getProperty(path); - return safeCastProperty(path, value, byte[][].class); - } - - /** - * Retrieves a repeated {@link GenericDocument} property by path. - * - *

See {@link #getProperty} for a detailed description of the path syntax. - * - * @param path The path to look for. - * @return The {@link GenericDocument}[] associated with the given path, or {@code null} if no - * value is set or the value is of a different type. - */ - @SuppressLint("ArrayReturn") - @Nullable - public GenericDocument[] getPropertyDocumentArray(@NonNull String path) { - Objects.requireNonNull(path); - Object value = getProperty(path); - return safeCastProperty(path, value, GenericDocument[].class); - } - - /** - * Casts a repeated property to the provided type, logging an error and returning {@code null} - * if the cast fails. - * - * @param path Path to the property within the document. Used for logging. - * @param value Value of the property - * @param tClass Class to cast the value into - */ - @Nullable - private static T safeCastProperty( - @NonNull String path, @Nullable Object value, @NonNull Class tClass) { - if (value == null) { - return null; - } - try { - return tClass.cast(value); - } catch (ClassCastException e) { - Log.w(TAG, "Error casting to requested type for path \"" + path + "\"", e); - return null; - } - } - - /** - * Copies the contents of this {@link GenericDocument} into a new {@link - * GenericDocument.Builder}. - * - *

The returned builder is a deep copy whose data is separate from this document. - * - * @hide - */ - // TODO(b/171882200): Expose this API in Android T - @NonNull - public GenericDocument.Builder> toBuilder() { - Bundle clonedBundle = BundleUtil.deepCopy(mBundle); - return new GenericDocument.Builder<>(clonedBundle); - } - - @Override - public boolean equals(@Nullable Object other) { - if (this == other) { - return true; - } - if (!(other instanceof GenericDocument)) { - return false; - } - GenericDocument otherDocument = (GenericDocument) other; - return BundleUtil.deepEquals(this.mBundle, otherDocument.mBundle); - } - - @Override - public int hashCode() { - if (mHashCode == null) { - mHashCode = BundleUtil.deepHashCode(mBundle); - } - return mHashCode; - } - - @Override - @NonNull - public String toString() { - IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); - appendGenericDocumentString(stringBuilder); - return stringBuilder.toString(); - } - - /** - * Appends a debug string for the {@link GenericDocument} instance to the given string builder. - * - * @param builder the builder to append to. - */ - void appendGenericDocumentString(@NonNull IndentingStringBuilder builder) { - Objects.requireNonNull(builder); - - builder.append("{\n"); - builder.increaseIndentLevel(); - - builder.append("namespace: \"").append(getNamespace()).append("\",\n"); - builder.append("id: \"").append(getId()).append("\",\n"); - builder.append("score: ").append(getScore()).append(",\n"); - builder.append("schemaType: \"").append(getSchemaType()).append("\",\n"); - builder.append("creationTimestampMillis: ") - .append(getCreationTimestampMillis()) - .append(",\n"); - builder.append("timeToLiveMillis: ").append(getTtlMillis()).append(",\n"); - - builder.append("properties: {\n"); - - String[] sortedProperties = getPropertyNames().toArray(new String[0]); - Arrays.sort(sortedProperties); - - for (int i = 0; i < sortedProperties.length; i++) { - Object property = getProperty(sortedProperties[i]); - builder.increaseIndentLevel(); - appendPropertyString(sortedProperties[i], property, builder); - if (i != sortedProperties.length - 1) { - builder.append(",\n"); - } - builder.decreaseIndentLevel(); - } - - builder.append("\n"); - builder.append("}"); - - builder.decreaseIndentLevel(); - builder.append("\n"); - builder.append("}"); - } - - /** - * Appends a debug string for the given document property to the given string builder. - * - * @param propertyName name of property to create string for. - * @param property property object to create string for. - * @param builder the builder to append to. - */ - private void appendPropertyString( - @NonNull String propertyName, - @NonNull Object property, - @NonNull IndentingStringBuilder builder) { - Objects.requireNonNull(propertyName); - Objects.requireNonNull(property); - Objects.requireNonNull(builder); - - builder.append("\"").append(propertyName).append("\": ["); - if (property instanceof GenericDocument[]) { - GenericDocument[] documentValues = (GenericDocument[]) property; - for (int i = 0; i < documentValues.length; ++i) { - builder.append("\n"); - builder.increaseIndentLevel(); - documentValues[i].appendGenericDocumentString(builder); - if (i != documentValues.length - 1) { - builder.append(","); - } - builder.append("\n"); - builder.decreaseIndentLevel(); - } - builder.append("]"); - } else { - int propertyArrLength = Array.getLength(property); - for (int i = 0; i < propertyArrLength; i++) { - Object propertyElement = Array.get(property, i); - if (propertyElement instanceof String) { - builder.append("\"").append((String) propertyElement).append("\""); - } else if (propertyElement instanceof byte[]) { - builder.append(Arrays.toString((byte[]) propertyElement)); - } else { - builder.append(propertyElement.toString()); - } - if (i != propertyArrLength - 1) { - builder.append(", "); - } else { - builder.append("]"); - } - } - } - } - - /** - * The builder class for {@link GenericDocument}. - * - * @param Type of subclass who extends this. - */ - // This builder is specifically designed to be extended by classes deriving from - // GenericDocument. - @SuppressLint("StaticFinalBuilder") - public static class Builder { - private Bundle mBundle; - private Bundle mProperties; - private final BuilderType mBuilderTypeInstance; - private boolean mBuilt = false; - - /** - * Creates a new {@link GenericDocument.Builder}. - * - *

Document IDs are unique within a namespace. - * - *

The number of namespaces per app should be kept small for efficiency reasons. - * - * @param namespace the namespace to set for the {@link GenericDocument}. - * @param id the unique identifier for the {@link GenericDocument} in its namespace. - * @param schemaType the {@link AppSearchSchema} type of the {@link GenericDocument}. The - * provided {@code schemaType} must be defined using {@link AppSearchSession#setSchema} - * prior to inserting a document of this {@code schemaType} into the AppSearch index - * using {@link AppSearchSession#put}. Otherwise, the document will be rejected by - * {@link AppSearchSession#put} with result code {@link - * AppSearchResult#RESULT_NOT_FOUND}. - */ - @SuppressWarnings("unchecked") - public Builder(@NonNull String namespace, @NonNull String id, @NonNull String schemaType) { - Objects.requireNonNull(namespace); - Objects.requireNonNull(id); - Objects.requireNonNull(schemaType); - - mBundle = new Bundle(); - mBuilderTypeInstance = (BuilderType) this; - mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace); - mBundle.putString(GenericDocument.ID_FIELD, id); - mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType); - mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS); - mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE); - - mProperties = new Bundle(); - mBundle.putBundle(PROPERTIES_FIELD, mProperties); - } - - /** - * Creates a new {@link GenericDocument.Builder} from the given Bundle. - * - *

The bundle is NOT copied. - */ - @SuppressWarnings("unchecked") - Builder(@NonNull Bundle bundle) { - mBundle = Objects.requireNonNull(bundle); - mProperties = mBundle.getBundle(PROPERTIES_FIELD); - mBuilderTypeInstance = (BuilderType) this; - } - - /** - * Sets the app-defined namespace this document resides in, changing the value provided in - * the constructor. No special values are reserved or understood by the infrastructure. - * - *

Document IDs are unique within a namespace. - * - *

The number of namespaces per app should be kept small for efficiency reasons. - * - * @hide - */ - @NonNull - public BuilderType setNamespace(@NonNull String namespace) { - Objects.requireNonNull(namespace); - resetIfBuilt(); - mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace); - return mBuilderTypeInstance; - } - - /** - * Sets the ID of this document, changing the value provided in the constructor. No special - * values are reserved or understood by the infrastructure. - * - *

Document IDs are unique within a namespace. - * - * @hide - */ - @NonNull - public BuilderType setId(@NonNull String id) { - Objects.requireNonNull(id); - resetIfBuilt(); - mBundle.putString(GenericDocument.ID_FIELD, id); - return mBuilderTypeInstance; - } - - /** - * Sets the schema type of this document, changing the value provided in the constructor. - * - *

To successfully index a document, the schema type must match the name of an {@link - * AppSearchSchema} object previously provided to {@link AppSearchSession#setSchema}. - * - * @hide - */ - @NonNull - public BuilderType setSchemaType(@NonNull String schemaType) { - Objects.requireNonNull(schemaType); - resetIfBuilt(); - mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType); - return mBuilderTypeInstance; - } - - /** - * Sets the score of the {@link GenericDocument}. - * - *

The score is a query-independent measure of the document's quality, relative to other - * {@link GenericDocument} objects of the same {@link AppSearchSchema} type. - * - *

Results may be sorted by score using {@link SearchSpec.Builder#setRankingStrategy}. - * Documents with higher scores are considered better than documents with lower scores. - * - *

Any non-negative integer can be used a score. By default, scores are set to 0. - * - * @param score any non-negative {@code int} representing the document's score. - */ - @NonNull - public BuilderType setScore(@IntRange(from = 0, to = Integer.MAX_VALUE) int score) { - if (score < 0) { - throw new IllegalArgumentException("Document score cannot be negative."); - } - resetIfBuilt(); - mBundle.putInt(GenericDocument.SCORE_FIELD, score); - return mBuilderTypeInstance; - } - - /** - * Sets the creation timestamp of the {@link GenericDocument}, in milliseconds. - * - *

This should be set using a value obtained from the {@link System#currentTimeMillis} - * time base. - * - *

If this method is not called, this will be set to the time the object is built. - * - * @param creationTimestampMillis a creation timestamp in milliseconds. - */ - @NonNull - public BuilderType setCreationTimestampMillis( - @CurrentTimeMillisLong long creationTimestampMillis) { - resetIfBuilt(); - mBundle.putLong( - GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, creationTimestampMillis); - return mBuilderTypeInstance; - } - - /** - * Sets the TTL (time-to-live) of the {@link GenericDocument}, in milliseconds. - * - *

The TTL is measured against {@link #getCreationTimestampMillis}. At the timestamp of - * {@code creationTimestampMillis + ttlMillis}, measured in the {@link - * System#currentTimeMillis} time base, the document will be auto-deleted. - * - *

The default value is 0, which means the document is permanent and won't be - * auto-deleted until the app is uninstalled or {@link AppSearchSession#remove} is called. - * - * @param ttlMillis a non-negative duration in milliseconds. - */ - @NonNull - public BuilderType setTtlMillis(long ttlMillis) { - if (ttlMillis < 0) { - throw new IllegalArgumentException("Document ttlMillis cannot be negative."); - } - resetIfBuilt(); - mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, ttlMillis); - return mBuilderTypeInstance; - } - - /** - * Sets one or multiple {@code String} values for a property, replacing its previous values. - * - * @param name the name associated with the {@code values}. Must match the name for this - * property as given in {@link AppSearchSchema.PropertyConfig#getName}. - * @param values the {@code String} values of the property. - * @throws IllegalArgumentException if no values are provided, or if a passed in {@code - * String} is {@code null}. - */ - @NonNull - public BuilderType setPropertyString(@NonNull String name, @NonNull String... values) { - Objects.requireNonNull(name); - Objects.requireNonNull(values); - resetIfBuilt(); - putInPropertyBundle(name, values); - return mBuilderTypeInstance; - } - - /** - * Sets one or multiple {@code boolean} values for a property, replacing its previous - * values. - * - * @param name the name associated with the {@code values}. Must match the name for this - * property as given in {@link AppSearchSchema.PropertyConfig#getName}. - * @param values the {@code boolean} values of the property. - */ - @NonNull - public BuilderType setPropertyBoolean(@NonNull String name, @NonNull boolean... values) { - Objects.requireNonNull(name); - Objects.requireNonNull(values); - resetIfBuilt(); - putInPropertyBundle(name, values); - return mBuilderTypeInstance; - } - - /** - * Sets one or multiple {@code long} values for a property, replacing its previous values. - * - * @param name the name associated with the {@code values}. Must match the name for this - * property as given in {@link AppSearchSchema.PropertyConfig#getName}. - * @param values the {@code long} values of the property. - */ - @NonNull - public BuilderType setPropertyLong(@NonNull String name, @NonNull long... values) { - Objects.requireNonNull(name); - Objects.requireNonNull(values); - resetIfBuilt(); - putInPropertyBundle(name, values); - return mBuilderTypeInstance; - } - - /** - * Sets one or multiple {@code double} values for a property, replacing its previous values. - * - * @param name the name associated with the {@code values}. Must match the name for this - * property as given in {@link AppSearchSchema.PropertyConfig#getName}. - * @param values the {@code double} values of the property. - */ - @NonNull - public BuilderType setPropertyDouble(@NonNull String name, @NonNull double... values) { - Objects.requireNonNull(name); - Objects.requireNonNull(values); - resetIfBuilt(); - putInPropertyBundle(name, values); - return mBuilderTypeInstance; - } - - /** - * Sets one or multiple {@code byte[]} for a property, replacing its previous values. - * - * @param name the name associated with the {@code values}. Must match the name for this - * property as given in {@link AppSearchSchema.PropertyConfig#getName}. - * @param values the {@code byte[]} of the property. - * @throws IllegalArgumentException if no values are provided, or if a passed in {@code - * byte[]} is {@code null}. - */ - @NonNull - public BuilderType setPropertyBytes(@NonNull String name, @NonNull byte[]... values) { - Objects.requireNonNull(name); - Objects.requireNonNull(values); - resetIfBuilt(); - putInPropertyBundle(name, values); - return mBuilderTypeInstance; - } - - /** - * Sets one or multiple {@link GenericDocument} values for a property, replacing its - * previous values. - * - * @param name the name associated with the {@code values}. Must match the name for this - * property as given in {@link AppSearchSchema.PropertyConfig#getName}. - * @param values the {@link GenericDocument} values of the property. - * @throws IllegalArgumentException if no values are provided, or if a passed in {@link - * GenericDocument} is {@code null}. - */ - @NonNull - public BuilderType setPropertyDocument( - @NonNull String name, @NonNull GenericDocument... values) { - Objects.requireNonNull(name); - Objects.requireNonNull(values); - resetIfBuilt(); - putInPropertyBundle(name, values); - return mBuilderTypeInstance; - } - - /** - * Clears the value for the property with the given name. - * - *

Note that this method does not support property paths. - * - * @param name The name of the property to clear. - * @hide - */ - @NonNull - public BuilderType clearProperty(@NonNull String name) { - Objects.requireNonNull(name); - resetIfBuilt(); - mProperties.remove(name); - return mBuilderTypeInstance; - } - - private void putInPropertyBundle(@NonNull String name, @NonNull String[] values) - throws IllegalArgumentException { - for (int i = 0; i < values.length; i++) { - if (values[i] == null) { - throw new IllegalArgumentException("The String at " + i + " is null."); - } - } - mProperties.putStringArray(name, values); - } - - private void putInPropertyBundle(@NonNull String name, @NonNull boolean[] values) { - mProperties.putBooleanArray(name, values); - } - - private void putInPropertyBundle(@NonNull String name, @NonNull double[] values) { - mProperties.putDoubleArray(name, values); - } - - private void putInPropertyBundle(@NonNull String name, @NonNull long[] values) { - mProperties.putLongArray(name, values); - } - - /** - * Converts and saves a byte[][] into {@link #mProperties}. - * - *

Bundle doesn't support for two dimension array byte[][], we are converting byte[][] - * into ArrayList, and each elements will contain a one dimension byte[]. - */ - private void putInPropertyBundle(@NonNull String name, @NonNull byte[][] values) { - ArrayList bundles = new ArrayList<>(values.length); - for (int i = 0; i < values.length; i++) { - if (values[i] == null) { - throw new IllegalArgumentException("The byte[] at " + i + " is null."); - } - Bundle bundle = new Bundle(); - bundle.putByteArray(BYTE_ARRAY_FIELD, values[i]); - bundles.add(bundle); - } - mProperties.putParcelableArrayList(name, bundles); - } - - private void putInPropertyBundle(@NonNull String name, @NonNull GenericDocument[] values) { - Parcelable[] documentBundles = new Parcelable[values.length]; - for (int i = 0; i < values.length; i++) { - if (values[i] == null) { - throw new IllegalArgumentException("The document at " + i + " is null."); - } - documentBundles[i] = values[i].mBundle; - } - mProperties.putParcelableArray(name, documentBundles); - } - - /** Builds the {@link GenericDocument} object. */ - @NonNull - public GenericDocument build() { - mBuilt = true; - // Set current timestamp for creation timestamp by default. - if (mBundle.getLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, -1) == -1) { - mBundle.putLong( - GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, - System.currentTimeMillis()); - } - return new GenericDocument(mBundle); - } - - private void resetIfBuilt() { - if (mBuilt) { - mBundle = BundleUtil.deepCopy(mBundle); - mProperties = mBundle.getBundle(PROPERTIES_FIELD); - mBuilt = false; - } - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java deleted file mode 100644 index 558899e493ae2..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GetByDocumentIdRequest.java +++ /dev/null @@ -1,181 +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 android.annotation.NonNull; -import android.util.ArrayMap; -import android.util.ArraySet; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * Encapsulates a request to retrieve documents by namespace and IDs from the {@link - * AppSearchSession} database. - * - * @see AppSearchSession#getByDocumentId - */ -public final class GetByDocumentIdRequest { - /** - * Schema type to be used in {@link GetByDocumentIdRequest.Builder#addProjection} to apply - * property paths to all results, excepting any types that have had their own, specific property - * paths set. - */ - public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*"; - - private final String mNamespace; - private final Set mIds; - private final Map> mTypePropertyPathsMap; - - GetByDocumentIdRequest( - @NonNull String namespace, - @NonNull Set ids, - @NonNull Map> typePropertyPathsMap) { - mNamespace = Objects.requireNonNull(namespace); - mIds = Objects.requireNonNull(ids); - mTypePropertyPathsMap = Objects.requireNonNull(typePropertyPathsMap); - } - - /** Returns the namespace attached to the request. */ - @NonNull - public String getNamespace() { - return mNamespace; - } - - /** Returns the set of document IDs attached to the request. */ - @NonNull - public Set getIds() { - return Collections.unmodifiableSet(mIds); - } - - /** - * Returns a map from schema type to property paths to be used for projection. - * - *

If the map is empty, then all properties will be retrieved for all results. - * - *

Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this - * function, rather than calling it multiple times. - */ - @NonNull - public Map> getProjections() { - Map> copy = new ArrayMap<>(); - for (Map.Entry> entry : mTypePropertyPathsMap.entrySet()) { - copy.put(entry.getKey(), new ArrayList<>(entry.getValue())); - } - return copy; - } - - /** - * Returns a map from schema type to property paths to be used for projection. - * - *

If the map is empty, then all properties will be retrieved for all results. - * - *

A more efficient version of {@link #getProjections}, but it returns a modifiable map. This - * is not meant to be unhidden and should only be used by internal classes. - * - * @hide - */ - @NonNull - public Map> getProjectionsInternal() { - return mTypePropertyPathsMap; - } - - /** Builder for {@link GetByDocumentIdRequest} objects. */ - public static final class Builder { - private final String mNamespace; - private ArraySet mIds = new ArraySet<>(); - private ArrayMap> mProjectionTypePropertyPaths = new ArrayMap<>(); - private boolean mBuilt = false; - - /** Creates a {@link GetByDocumentIdRequest.Builder} instance. */ - public Builder(@NonNull String namespace) { - mNamespace = Objects.requireNonNull(namespace); - } - - /** Adds one or more document IDs to the request. */ - @NonNull - public Builder addIds(@NonNull String... ids) { - Objects.requireNonNull(ids); - resetIfBuilt(); - return addIds(Arrays.asList(ids)); - } - - /** Adds a collection of IDs to the request. */ - @NonNull - public Builder addIds(@NonNull Collection ids) { - Objects.requireNonNull(ids); - resetIfBuilt(); - mIds.addAll(ids); - return this; - } - - /** - * Adds property paths for the specified type to be used for projection. If property paths - * are added for a type, then only the properties referred to will be retrieved for results - * of that type. If a property path that is specified isn't present in a result, it will be - * ignored for that result. Property paths cannot be null. - * - *

If no property paths are added for a particular type, then all properties of results - * of that type will be retrieved. - * - *

If property path is added for the {@link - * GetByDocumentIdRequest#PROJECTION_SCHEMA_TYPE_WILDCARD}, then those property paths will - * apply to all results, excepting any types that have their own, specific property paths - * set. - * - * @see SearchSpec.Builder#addProjection - */ - @NonNull - public Builder addProjection( - @NonNull String schemaType, @NonNull Collection propertyPaths) { - Objects.requireNonNull(schemaType); - Objects.requireNonNull(propertyPaths); - resetIfBuilt(); - List propertyPathsList = new ArrayList<>(propertyPaths.size()); - for (String propertyPath : propertyPaths) { - Objects.requireNonNull(propertyPath); - propertyPathsList.add(propertyPath); - } - mProjectionTypePropertyPaths.put(schemaType, propertyPathsList); - return this; - } - - /** Builds a new {@link GetByDocumentIdRequest}. */ - @NonNull - public GetByDocumentIdRequest build() { - mBuilt = true; - return new GetByDocumentIdRequest(mNamespace, mIds, mProjectionTypePropertyPaths); - } - - private void resetIfBuilt() { - if (mBuilt) { - mIds = new ArraySet<>(mIds); - // No need to clone each propertyPathsList inside mProjectionTypePropertyPaths since - // the builder only replaces it, never adds to it. So even if the builder is used - // again, the previous one will remain with the object. - mProjectionTypePropertyPaths = new ArrayMap<>(mProjectionTypePropertyPaths); - mBuilt = false; - } - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java deleted file mode 100644 index 018f16de28cb0..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/GetSchemaResponse.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch; - -import android.annotation.IntRange; -import android.annotation.NonNull; -import android.os.Bundle; -import android.util.ArraySet; - -import java.util.ArrayList; -import java.util.Objects; -import java.util.Set; - -/** The response class of {@link AppSearchSession#getSchema} */ -public final class GetSchemaResponse { - private static final String VERSION_FIELD = "version"; - private static final String SCHEMAS_FIELD = "schemas"; - - private final Bundle mBundle; - - GetSchemaResponse(@NonNull Bundle bundle) { - mBundle = Objects.requireNonNull(bundle); - } - - /** - * Returns the {@link Bundle} populated by this builder. - * - * @hide - */ - @NonNull - public Bundle getBundle() { - return mBundle; - } - - /** - * Returns the overall database schema version. - * - *

If the database is empty, 0 will be returned. - */ - @IntRange(from = 0) - public int getVersion() { - return mBundle.getInt(VERSION_FIELD); - } - - /** - * Return the schemas most recently successfully provided to {@link AppSearchSession#setSchema}. - * - *

It is inefficient to call this method repeatedly. - */ - @NonNull - public Set getSchemas() { - ArrayList schemaBundles = mBundle.getParcelableArrayList(SCHEMAS_FIELD); - Set schemas = new ArraySet<>(schemaBundles.size()); - for (int i = 0; i < schemaBundles.size(); i++) { - schemas.add(new AppSearchSchema(schemaBundles.get(i))); - } - return schemas; - } - - /** Builder for {@link GetSchemaResponse} objects. */ - public static final class Builder { - private int mVersion = 0; - private ArrayList mSchemaBundles = new ArrayList<>(); - private boolean mBuilt = false; - - /** - * Sets the database overall schema version. - * - *

Default version is 0 - */ - @NonNull - public Builder setVersion(@IntRange(from = 0) int version) { - resetIfBuilt(); - mVersion = version; - return this; - } - - /** Adds one {@link AppSearchSchema} to the schema list. */ - @NonNull - public Builder addSchema(@NonNull AppSearchSchema schema) { - Objects.requireNonNull(schema); - resetIfBuilt(); - mSchemaBundles.add(schema.getBundle()); - return this; - } - - /** Builds a {@link GetSchemaResponse} object. */ - @NonNull - public GetSchemaResponse build() { - Bundle bundle = new Bundle(); - bundle.putInt(VERSION_FIELD, mVersion); - bundle.putParcelableArrayList(SCHEMAS_FIELD, mSchemaBundles); - mBuilt = true; - return new GetSchemaResponse(bundle); - } - - private void resetIfBuilt() { - if (mBuilt) { - mSchemaBundles = new ArrayList<>(mSchemaBundles); - mBuilt = false; - } - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/Migrator.java b/apex/appsearch/framework/java/external/android/app/appsearch/Migrator.java deleted file mode 100644 index c5a0f63c94b6b..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/Migrator.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch; - -import android.annotation.NonNull; -import android.annotation.WorkerThread; - -/** - * A migrator class to translate {@link GenericDocument} from different version of {@link - * AppSearchSchema} - * - *

Make non-backwards-compatible changes will delete all stored documents in old schema. You can - * save your documents by setting {@link Migrator} via the {@link - * SetSchemaRequest.Builder#setMigrator} for each type and target version you want to save. - * - *

{@link #onDowngrade} or {@link #onUpgrade} will be triggered if the version number of the - * schema stored in AppSearch is different with the version in the request. - * - *

If any error or Exception occurred in the {@link #onDowngrade} or {@link #onUpgrade}, all the - * setSchema request will be rejected unless the schema changes are backwards-compatible, and stored - * documents won't have any observable changes. - */ -public abstract class Migrator { - /** - * Returns {@code true} if this migrator's source type needs to be migrated to update from - * currentVersion to finalVersion. - * - *

Migration won't be triggered if currentVersion is equal to finalVersion even if {@link - * #shouldMigrate} return true; - */ - public abstract boolean shouldMigrate(int currentVersion, int finalVersion); - - /** - * Migrates {@link GenericDocument} to a newer version of {@link AppSearchSchema}. - * - *

This method will be invoked only if the {@link SetSchemaRequest} is setting a higher - * version number than the current {@link AppSearchSchema} saved in AppSearch. - * - *

If this {@link Migrator} is provided to cover a compatible schema change via {@link - * AppSearchSession#setSchema}, documents under the old version won't be removed unless you use - * the same document ID. - * - *

This method will be invoked on the background worker thread provided via {@link - * AppSearchSession#setSchema}. - * - * @param currentVersion The current version of the document's schema. - * @param finalVersion The final version that documents need to be migrated to. - * @param document The {@link GenericDocument} need to be translated to new version. - * @return A {@link GenericDocument} in new version. - */ - @WorkerThread - @NonNull - public abstract GenericDocument onUpgrade( - int currentVersion, int finalVersion, @NonNull GenericDocument document); - - /** - * Migrates {@link GenericDocument} to an older version of {@link AppSearchSchema}. - * - *

This method will be invoked only if the {@link SetSchemaRequest} is setting a lower - * version number than the current {@link AppSearchSchema} saved in AppSearch. - * - *

If this {@link Migrator} is provided to cover a compatible schema change via {@link - * AppSearchSession#setSchema}, documents under the old version won't be removed unless you use - * the same document ID. - * - *

This method will be invoked on the background worker thread. - * - * @param currentVersion The current version of the document's schema. - * @param finalVersion The final version that documents need to be migrated to. - * @param document The {@link GenericDocument} need to be translated to new version. - * @return A {@link GenericDocument} in new version. - */ - @WorkerThread - @NonNull - public abstract GenericDocument onDowngrade( - int currentVersion, int finalVersion, @NonNull GenericDocument document); -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java b/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java deleted file mode 100644 index 4f63baeeb41cb..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/PackageIdentifier.java +++ /dev/null @@ -1,81 +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 android.annotation.NonNull; -import android.app.appsearch.util.BundleUtil; -import android.os.Bundle; - -import java.util.Objects; - -/** This class represents a uniquely identifiable package. */ -public class PackageIdentifier { - private static final String PACKAGE_NAME_FIELD = "packageName"; - private static final String SHA256_CERTIFICATE_FIELD = "sha256Certificate"; - - private final Bundle mBundle; - - /** - * Creates a unique identifier for a package. - * - * @param packageName Name of the package. - * @param sha256Certificate SHA256 certificate digest of the package. - */ - public PackageIdentifier(@NonNull String packageName, @NonNull byte[] sha256Certificate) { - mBundle = new Bundle(); - mBundle.putString(PACKAGE_NAME_FIELD, packageName); - mBundle.putByteArray(SHA256_CERTIFICATE_FIELD, sha256Certificate); - } - - /** @hide */ - public PackageIdentifier(@NonNull Bundle bundle) { - mBundle = Objects.requireNonNull(bundle); - } - - /** @hide */ - @NonNull - public Bundle getBundle() { - return mBundle; - } - - @NonNull - public String getPackageName() { - return Objects.requireNonNull(mBundle.getString(PACKAGE_NAME_FIELD)); - } - - @NonNull - public byte[] getSha256Certificate() { - return Objects.requireNonNull(mBundle.getByteArray(SHA256_CERTIFICATE_FIELD)); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || !(obj instanceof PackageIdentifier)) { - return false; - } - final PackageIdentifier other = (PackageIdentifier) obj; - return BundleUtil.deepEquals(mBundle, other.mBundle); - } - - @Override - public int hashCode() { - return BundleUtil.deepHashCode(mBundle); - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java deleted file mode 100644 index 342412882981b..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/PutDocumentsRequest.java +++ /dev/null @@ -1,84 +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 android.annotation.NonNull; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -/** - * Encapsulates a request to index documents into an {@link AppSearchSession} database. - * - * @see AppSearchSession#put - */ -public final class PutDocumentsRequest { - private final List mDocuments; - - PutDocumentsRequest(List documents) { - mDocuments = documents; - } - - /** Returns a list of {@link GenericDocument} objects that are part of this request. */ - @NonNull - public List getGenericDocuments() { - return Collections.unmodifiableList(mDocuments); - } - - /** Builder for {@link PutDocumentsRequest} objects. */ - public static final class Builder { - private ArrayList mDocuments = new ArrayList<>(); - private boolean mBuilt = false; - - /** Adds one or more {@link GenericDocument} objects to the request. */ - @NonNull - public Builder addGenericDocuments(@NonNull GenericDocument... documents) { - Objects.requireNonNull(documents); - resetIfBuilt(); - return addGenericDocuments(Arrays.asList(documents)); - } - - /** Adds a collection of {@link GenericDocument} objects to the request. */ - @NonNull - public Builder addGenericDocuments( - @NonNull Collection documents) { - Objects.requireNonNull(documents); - resetIfBuilt(); - mDocuments.addAll(documents); - return this; - } - - /** Creates a new {@link PutDocumentsRequest} object. */ - @NonNull - public PutDocumentsRequest build() { - mBuilt = true; - return new PutDocumentsRequest(mDocuments); - } - - private void resetIfBuilt() { - if (mBuilt) { - mDocuments = new ArrayList<>(mDocuments); - mBuilt = false; - } - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByDocumentIdRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByDocumentIdRequest.java deleted file mode 100644 index b86fd27dd1f40..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/RemoveByDocumentIdRequest.java +++ /dev/null @@ -1,97 +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 android.annotation.NonNull; -import android.util.ArraySet; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Objects; -import java.util.Set; - -/** - * Encapsulates a request to remove documents by namespace and IDs from the {@link AppSearchSession} - * database. - * - * @see AppSearchSession#remove - */ -public final class RemoveByDocumentIdRequest { - private final String mNamespace; - private final Set mIds; - - RemoveByDocumentIdRequest(String namespace, Set ids) { - mNamespace = namespace; - mIds = ids; - } - - /** Returns the namespace to remove documents from. */ - @NonNull - public String getNamespace() { - return mNamespace; - } - - /** Returns the set of document IDs attached to the request. */ - @NonNull - public Set getIds() { - return Collections.unmodifiableSet(mIds); - } - - /** Builder for {@link RemoveByDocumentIdRequest} objects. */ - public static final class Builder { - private final String mNamespace; - private ArraySet mIds = new ArraySet<>(); - private boolean mBuilt = false; - - /** Creates a {@link RemoveByDocumentIdRequest.Builder} instance. */ - public Builder(@NonNull String namespace) { - mNamespace = Objects.requireNonNull(namespace); - } - - /** Adds one or more document IDs to the request. */ - @NonNull - public Builder addIds(@NonNull String... ids) { - Objects.requireNonNull(ids); - resetIfBuilt(); - return addIds(Arrays.asList(ids)); - } - - /** Adds a collection of IDs to the request. */ - @NonNull - public Builder addIds(@NonNull Collection ids) { - Objects.requireNonNull(ids); - resetIfBuilt(); - mIds.addAll(ids); - return this; - } - - /** Builds a new {@link RemoveByDocumentIdRequest}. */ - @NonNull - public RemoveByDocumentIdRequest build() { - mBuilt = true; - return new RemoveByDocumentIdRequest(mNamespace, mIds); - } - - private void resetIfBuilt() { - if (mBuilt) { - mIds = new ArraySet<>(mIds); - mBuilt = false; - } - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java deleted file mode 100644 index 26bdf031a5b0e..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportSystemUsageRequest.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch; - -import android.annotation.CurrentTimeMillisLong; -import android.annotation.NonNull; - -import java.util.Objects; - -/** - * A request to report usage of a document owned by another app from a system UI surface. - * - *

Usage reported in this way is measured separately from usage reported via {@link - * AppSearchSession#reportUsage}. - * - *

See {@link GlobalSearchSession#reportSystemUsage} for a detailed description of usage - * reporting. - */ -public final class ReportSystemUsageRequest { - private final String mPackageName; - private final String mDatabase; - private final String mNamespace; - private final String mDocumentId; - private final long mUsageTimestampMillis; - - ReportSystemUsageRequest( - @NonNull String packageName, - @NonNull String database, - @NonNull String namespace, - @NonNull String documentId, - long usageTimestampMillis) { - mPackageName = Objects.requireNonNull(packageName); - mDatabase = Objects.requireNonNull(database); - mNamespace = Objects.requireNonNull(namespace); - mDocumentId = Objects.requireNonNull(documentId); - mUsageTimestampMillis = usageTimestampMillis; - } - - /** Returns the package name of the app which owns the document that was used. */ - @NonNull - public String getPackageName() { - return mPackageName; - } - - /** Returns the database in which the document that was used resides. */ - @NonNull - public String getDatabaseName() { - return mDatabase; - } - - /** Returns the namespace of the document that was used. */ - @NonNull - public String getNamespace() { - return mNamespace; - } - - /** Returns the ID of document that was used. */ - @NonNull - public String getDocumentId() { - return mDocumentId; - } - - /** - * Returns the timestamp in milliseconds of the usage report (the time at which the document was - * used). - * - *

The value is in the {@link System#currentTimeMillis} time base. - */ - @CurrentTimeMillisLong - public long getUsageTimestampMillis() { - return mUsageTimestampMillis; - } - - /** Builder for {@link ReportSystemUsageRequest} objects. */ - public static final class Builder { - private final String mPackageName; - private final String mDatabase; - private final String mNamespace; - private final String mDocumentId; - private Long mUsageTimestampMillis; - - /** - * 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( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String namespace, - @NonNull String documentId) { - mPackageName = Objects.requireNonNull(packageName); - mDatabase = Objects.requireNonNull(databaseName); - mNamespace = Objects.requireNonNull(namespace); - mDocumentId = Objects.requireNonNull(documentId); - } - - /** - * Sets the timestamp in milliseconds of the usage report (the time at which the document - * was used). - * - *

The value is in the {@link System#currentTimeMillis} time base. - * - *

If unset, this defaults to the current timestamp at the time that the {@link - * ReportSystemUsageRequest} is constructed. - */ - @NonNull - public ReportSystemUsageRequest.Builder setUsageTimestampMillis( - @CurrentTimeMillisLong long usageTimestampMillis) { - mUsageTimestampMillis = usageTimestampMillis; - return this; - } - - /** Builds a new {@link ReportSystemUsageRequest}. */ - @NonNull - public ReportSystemUsageRequest build() { - if (mUsageTimestampMillis == null) { - mUsageTimestampMillis = System.currentTimeMillis(); - } - return new ReportSystemUsageRequest( - mPackageName, mDatabase, mNamespace, mDocumentId, mUsageTimestampMillis); - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java deleted file mode 100644 index e807803bae39e..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/ReportUsageRequest.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch; - -import android.annotation.CurrentTimeMillisLong; -import android.annotation.NonNull; - -import java.util.Objects; - -/** - * A request to report usage of a document. - * - *

See {@link AppSearchSession#reportUsage} for a detailed description of usage reporting. - * - * @see AppSearchSession#reportUsage - */ -public final class ReportUsageRequest { - private final String mNamespace; - private final String mDocumentId; - private final long mUsageTimestampMillis; - - ReportUsageRequest( - @NonNull String namespace, @NonNull String documentId, long usageTimestampMillis) { - mNamespace = Objects.requireNonNull(namespace); - mDocumentId = Objects.requireNonNull(documentId); - mUsageTimestampMillis = usageTimestampMillis; - } - - /** Returns the namespace of the document that was used. */ - @NonNull - public String getNamespace() { - return mNamespace; - } - - /** Returns the ID of document that was used. */ - @NonNull - public String getDocumentId() { - return mDocumentId; - } - - /** - * Returns the timestamp in milliseconds of the usage report (the time at which the document was - * used). - * - *

The value is in the {@link System#currentTimeMillis} time base. - */ - @CurrentTimeMillisLong - public long getUsageTimestampMillis() { - return mUsageTimestampMillis; - } - - /** Builder for {@link ReportUsageRequest} objects. */ - public static final class Builder { - private final String mNamespace; - private final String mDocumentId; - private Long mUsageTimestampMillis; - - /** - * 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) { - mNamespace = Objects.requireNonNull(namespace); - mDocumentId = Objects.requireNonNull(documentId); - } - - /** - * Sets the timestamp in milliseconds of the usage report (the time at which the document - * was used). - * - *

The value is in the {@link System#currentTimeMillis} time base. - * - *

If unset, this defaults to the current timestamp at the time that the {@link - * ReportUsageRequest} is constructed. - */ - @NonNull - public ReportUsageRequest.Builder setUsageTimestampMillis( - @CurrentTimeMillisLong long usageTimestampMillis) { - mUsageTimestampMillis = usageTimestampMillis; - return this; - } - - /** Builds a new {@link ReportUsageRequest}. */ - @NonNull - public ReportUsageRequest build() { - if (mUsageTimestampMillis == null) { - mUsageTimestampMillis = System.currentTimeMillis(); - } - return new ReportUsageRequest(mNamespace, mDocumentId, mUsageTimestampMillis); - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java deleted file mode 100644 index f6a597c51116e..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResult.java +++ /dev/null @@ -1,549 +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 android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Bundle; - -import com.android.internal.util.Preconditions; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * This class represents one of the results obtained from an AppSearch query. - * - *

This allows clients to obtain: - * - *

    - *
  • The document which matched, using {@link #getGenericDocument} - *
  • Information about which properties in the document matched, and "snippet" information - * containing textual summaries of the document's matches, using {@link #getMatchInfos} - *
- * - *

"Snippet" refers to a substring of text from the content of document that is returned as a - * part of search result. - * - * @see SearchResults - */ -public final class SearchResult { - static final String DOCUMENT_FIELD = "document"; - static final String MATCH_INFOS_FIELD = "matchInfos"; - static final String PACKAGE_NAME_FIELD = "packageName"; - static final String DATABASE_NAME_FIELD = "databaseName"; - static final String RANKING_SIGNAL_FIELD = "rankingSignal"; - - @NonNull private final Bundle mBundle; - - /** Cache of the inflated document. Comes from inflating mDocumentBundle at first use. */ - @Nullable private GenericDocument mDocument; - - /** Cache of the inflated matches. Comes from inflating mMatchBundles at first use. */ - @Nullable private List mMatchInfos; - - /** @hide */ - public SearchResult(@NonNull Bundle bundle) { - mBundle = Objects.requireNonNull(bundle); - } - - /** @hide */ - @NonNull - public Bundle getBundle() { - return mBundle; - } - - /** - * Contains the matching {@link GenericDocument}. - * - * @return Document object which matched the query. - */ - @NonNull - public GenericDocument getGenericDocument() { - if (mDocument == null) { - mDocument = - new GenericDocument(Objects.requireNonNull(mBundle.getBundle(DOCUMENT_FIELD))); - } - return mDocument; - } - - /** - * Returns a list of {@link MatchInfo}s providing information about how the document in {@link - * #getGenericDocument} matched the query. - * - * @return List of matches based on {@link SearchSpec}. If snippeting is disabled using {@link - * SearchSpec.Builder#setSnippetCount} or {@link - * SearchSpec.Builder#setSnippetCountPerProperty}, for all results after that value, this - * method returns an empty list. - */ - @NonNull - public List getMatchInfos() { - if (mMatchInfos == null) { - List matchBundles = - Objects.requireNonNull(mBundle.getParcelableArrayList(MATCH_INFOS_FIELD)); - mMatchInfos = new ArrayList<>(matchBundles.size()); - for (int i = 0; i < matchBundles.size(); i++) { - MatchInfo matchInfo = new MatchInfo(matchBundles.get(i), getGenericDocument()); - mMatchInfos.add(matchInfo); - } - } - return mMatchInfos; - } - - /** - * Contains the package name of the app that stored the {@link GenericDocument}. - * - * @return Package name that stored the document - */ - @NonNull - public String getPackageName() { - return Objects.requireNonNull(mBundle.getString(PACKAGE_NAME_FIELD)); - } - - /** - * Contains the database name that stored the {@link GenericDocument}. - * - * @return Name of the database within which the document is stored - */ - @NonNull - public String getDatabaseName() { - return Objects.requireNonNull(mBundle.getString(DATABASE_NAME_FIELD)); - } - - /** - * Returns the ranking signal of the {@link GenericDocument}, according to the ranking strategy - * set in {@link SearchSpec.Builder#setRankingStrategy(int)}. - * - *

The meaning of the ranking signal and its value is determined by the selected ranking - * strategy: - * - *

    - *
  • {@link SearchSpec#RANKING_STRATEGY_NONE} - this value will be 0 - *
  • {@link SearchSpec#RANKING_STRATEGY_DOCUMENT_SCORE} - the value returned by calling - * {@link GenericDocument#getScore()} on the document returned by {@link - * #getGenericDocument()} - *
  • {@link SearchSpec#RANKING_STRATEGY_CREATION_TIMESTAMP} - the value returned by calling - * {@link GenericDocument#getCreationTimestampMillis()} on the document returned by {@link - * #getGenericDocument()} - *
  • {@link SearchSpec#RANKING_STRATEGY_RELEVANCE_SCORE} - an arbitrary double value where a - * higher value means more relevant - *
  • {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} - the number of times usage has been - * reported for the document returned by {@link #getGenericDocument()} - *
  • {@link SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} - the timestamp of the - * most recent usage that has been reported for the document returned by {@link - * #getGenericDocument()} - *
- * - * @return Ranking signal of the document - */ - public double getRankingSignal() { - return mBundle.getDouble(RANKING_SIGNAL_FIELD); - } - - /** Builder for {@link SearchResult} objects. */ - public static final class Builder { - private final String mPackageName; - private final String mDatabaseName; - private ArrayList mMatchInfoBundles = new ArrayList<>(); - private GenericDocument mGenericDocument; - private double mRankingSignal; - private boolean mBuilt = false; - - /** - * Constructs a new builder for {@link SearchResult} objects. - * - * @param packageName the package name the matched document belongs to - * @param databaseName the database name the matched document belongs to. - */ - public Builder(@NonNull String packageName, @NonNull String databaseName) { - mPackageName = Objects.requireNonNull(packageName); - mDatabaseName = Objects.requireNonNull(databaseName); - } - - /** Sets the document which matched. */ - @NonNull - public Builder setGenericDocument(@NonNull GenericDocument document) { - Objects.requireNonNull(document); - resetIfBuilt(); - mGenericDocument = document; - return this; - } - - /** Adds another match to this SearchResult. */ - @NonNull - public Builder addMatchInfo(@NonNull MatchInfo matchInfo) { - Preconditions.checkState( - matchInfo.mDocument == null, - "This MatchInfo is already associated with a SearchResult and can't be " - + "reassigned"); - resetIfBuilt(); - mMatchInfoBundles.add(matchInfo.mBundle); - return this; - } - - /** Sets the ranking signal of the matched document in this SearchResult. */ - @NonNull - public Builder setRankingSignal(double rankingSignal) { - resetIfBuilt(); - mRankingSignal = rankingSignal; - return this; - } - - /** Constructs a new {@link SearchResult}. */ - @NonNull - public SearchResult build() { - Bundle bundle = new Bundle(); - bundle.putString(PACKAGE_NAME_FIELD, mPackageName); - bundle.putString(DATABASE_NAME_FIELD, mDatabaseName); - bundle.putBundle(DOCUMENT_FIELD, mGenericDocument.getBundle()); - bundle.putDouble(RANKING_SIGNAL_FIELD, mRankingSignal); - bundle.putParcelableArrayList(MATCH_INFOS_FIELD, mMatchInfoBundles); - mBuilt = true; - return new SearchResult(bundle); - } - - private void resetIfBuilt() { - if (mBuilt) { - mMatchInfoBundles = new ArrayList<>(mMatchInfoBundles); - mBuilt = false; - } - } - } - - /** - * This class represents a match objects for any Snippets that might be present in {@link - * SearchResults} from query. Using this class user can get the full text, exact matches and - * Snippets of document content for a given match. - * - *

Class Example 1: A document contains following text in property subject: - * - *

A commonly used fake word is foo. Another nonsense word that’s used a lot is bar. - * - *

If the queryExpression is "foo". - * - *

{@link MatchInfo#getPropertyPath()} returns "subject" - * - *

{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another - * nonsense word that’s used a lot is bar." - * - *

{@link MatchInfo#getExactMatchRange()} returns [29, 32] - * - *

{@link MatchInfo#getExactMatch()} returns "foo" - * - *

{@link MatchInfo#getSnippetRange()} returns [26, 33] - * - *

{@link MatchInfo#getSnippet()} returns "is foo." - * - *

- * - *

Class Example 2: A document contains a property name sender which contains 2 property - * names name and email, so we will have 2 property paths: {@code sender.name} and {@code - * sender.email}. - * - *

Let {@code sender.name = "Test Name Jr."} and {@code sender.email = - * "TestNameJr@gmail.com"} - * - *

If the queryExpression is "Test". We will have 2 matches. - * - *

Match-1 - * - *

{@link MatchInfo#getPropertyPath()} returns "sender.name" - * - *

{@link MatchInfo#getFullText()} returns "Test Name Jr." - * - *

{@link MatchInfo#getExactMatchRange()} returns [0, 4] - * - *

{@link MatchInfo#getExactMatch()} returns "Test" - * - *

{@link MatchInfo#getSnippetRange()} returns [0, 9] - * - *

{@link MatchInfo#getSnippet()} returns "Test Name" - * - *

Match-2 - * - *

{@link MatchInfo#getPropertyPath()} returns "sender.email" - * - *

{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com" - * - *

{@link MatchInfo#getExactMatchRange()} returns [0, 20] - * - *

{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com" - * - *

{@link MatchInfo#getSnippetRange()} returns [0, 20] - * - *

{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com" - */ - public static final class MatchInfo { - /** The path of the matching snippet property. */ - private static final String PROPERTY_PATH_FIELD = "propertyPath"; - - private static final String EXACT_MATCH_RANGE_LOWER_FIELD = "exactMatchRangeLower"; - private static final String EXACT_MATCH_RANGE_UPPER_FIELD = "exactMatchRangeUpper"; - private static final String SNIPPET_RANGE_LOWER_FIELD = "snippetRangeLower"; - private static final String SNIPPET_RANGE_UPPER_FIELD = "snippetRangeUpper"; - - private final String mPropertyPath; - final Bundle mBundle; - - /** - * Document which the match comes from. - * - *

If this is {@code null}, methods which require access to the document, like {@link - * #getExactMatch}, will throw {@link NullPointerException}. - */ - @Nullable final GenericDocument mDocument; - - /** Full text of the matched property. Populated on first use. */ - @Nullable private String mFullText; - - /** Range of property that exactly matched the query. Populated on first use. */ - @Nullable private MatchRange mExactMatchRange; - - /** Range of some reasonable amount of context around the query. Populated on first use. */ - @Nullable private MatchRange mWindowRange; - - MatchInfo(@NonNull Bundle bundle, @Nullable GenericDocument document) { - mBundle = Objects.requireNonNull(bundle); - mDocument = document; - mPropertyPath = Objects.requireNonNull(bundle.getString(PROPERTY_PATH_FIELD)); - } - - /** - * Gets the property path corresponding to the given entry. - * - *

A property path is a '.' - delimited sequence of property names indicating which - * property in the document these snippets correspond to. - * - *

Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. For class - * example 1 this returns "subject" - */ - @NonNull - public String getPropertyPath() { - return mPropertyPath; - } - - /** - * Gets the full text corresponding to the given entry. - * - *

For class example this returns "A commonly used fake word is foo. Another nonsense - * word that's used a lot is bar." - */ - @NonNull - public String getFullText() { - if (mFullText == null) { - Preconditions.checkState( - mDocument != null, - "Document has not been populated; this MatchInfo cannot be used yet"); - mFullText = getPropertyValues(mDocument, mPropertyPath); - } - return mFullText; - } - - /** - * Gets the exact {@link MatchRange} corresponding to the given entry. - * - *

For class example 1 this returns [29, 32] - */ - @NonNull - public MatchRange getExactMatchRange() { - if (mExactMatchRange == null) { - mExactMatchRange = - new MatchRange( - mBundle.getInt(EXACT_MATCH_RANGE_LOWER_FIELD), - mBundle.getInt(EXACT_MATCH_RANGE_UPPER_FIELD)); - } - return mExactMatchRange; - } - - /** - * Gets the {@link MatchRange} corresponding to the given entry. - * - *

For class example 1 this returns "foo" - */ - @NonNull - public CharSequence getExactMatch() { - return getSubstring(getExactMatchRange()); - } - - /** - * Gets the snippet {@link MatchRange} corresponding to the given entry. - * - *

Only populated when set maxSnippetSize > 0 in {@link - * SearchSpec.Builder#setMaxSnippetSize}. - * - *

For class example 1 this returns [29, 41]. - */ - @NonNull - public MatchRange getSnippetRange() { - if (mWindowRange == null) { - mWindowRange = - new MatchRange( - mBundle.getInt(SNIPPET_RANGE_LOWER_FIELD), - mBundle.getInt(SNIPPET_RANGE_UPPER_FIELD)); - } - return mWindowRange; - } - - /** - * Gets the snippet corresponding to the given entry. - * - *

Snippet - Provides a subset of the content to display. Only populated when requested - * maxSnippetSize > 0. The size of this content can be changed by {@link - * SearchSpec.Builder#setMaxSnippetSize}. Windowing is centered around the middle of the - * matched token with content on either side clipped to token boundaries. - * - *

For class example 1 this returns "foo. Another" - */ - @NonNull - public CharSequence getSnippet() { - return getSubstring(getSnippetRange()); - } - - private CharSequence getSubstring(MatchRange range) { - return getFullText().substring(range.getStart(), range.getEnd()); - } - - /** Extracts the matching string from the document. */ - private static String getPropertyValues(GenericDocument document, String propertyName) { - // In IcingLib snippeting is available for only 3 data types i.e String, double and - // long, so we need to check which of these three are requested. - // TODO (tytytyww): support double[] and long[]. - String result = document.getPropertyString(propertyName); - if (result == null) { - throw new IllegalStateException( - "No content found for requested property path: " + propertyName); - } - return result; - } - - /** Builder for {@link MatchInfo} objects. */ - public static final class Builder { - private final String mPropertyPath; - private MatchRange mExactMatchRange = new MatchRange(0, 0); - private MatchRange mSnippetRange = new MatchRange(0, 0); - - /** - * Creates a new {@link MatchInfo.Builder} reporting a match with the given property - * path. - * - *

A property path is a dot-delimited sequence of property names indicating which - * property in the document these snippets correspond to. - * - *

Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. - * For class example 1 this returns "subject". - * - * @param propertyPath A {@code dot-delimited sequence of property names indicating - * which property in the document these snippets correspond to. - */ - public Builder(@NonNull String propertyPath) { - mPropertyPath = Objects.requireNonNull(propertyPath); - } - - /** Sets the exact {@link MatchRange} corresponding to the given entry. */ - @NonNull - public Builder setExactMatchRange(@NonNull MatchRange matchRange) { - mExactMatchRange = Objects.requireNonNull(matchRange); - return this; - } - - /** Sets the snippet {@link MatchRange} corresponding to the given entry. */ - @NonNull - public Builder setSnippetRange(@NonNull MatchRange matchRange) { - mSnippetRange = Objects.requireNonNull(matchRange); - return this; - } - - /** Constructs a new {@link MatchInfo}. */ - @NonNull - public MatchInfo build() { - Bundle bundle = new Bundle(); - bundle.putString(SearchResult.MatchInfo.PROPERTY_PATH_FIELD, mPropertyPath); - bundle.putInt(MatchInfo.EXACT_MATCH_RANGE_LOWER_FIELD, mExactMatchRange.getStart()); - bundle.putInt(MatchInfo.EXACT_MATCH_RANGE_UPPER_FIELD, mExactMatchRange.getEnd()); - bundle.putInt(MatchInfo.SNIPPET_RANGE_LOWER_FIELD, mSnippetRange.getStart()); - bundle.putInt(MatchInfo.SNIPPET_RANGE_UPPER_FIELD, mSnippetRange.getEnd()); - return new MatchInfo(bundle, /*document=*/ null); - } - } - } - - /** - * Class providing the position range of matching information. - * - *

All ranges are finite, and the left side of the range is always {@code <=} the right side - * of the range. - * - *

Example: MatchRange(0, 100) represent a hundred ints from 0 to 99." - */ - public static final class MatchRange { - private final int mEnd; - private final int mStart; - - /** - * Creates a new immutable range. - * - *

The endpoints are {@code [start, end)}; that is the range is bounded. {@code start} - * must be lesser or equal to {@code end}. - * - * @param start The start point (inclusive) - * @param end The end point (exclusive) - */ - public MatchRange(int start, int end) { - if (start > end) { - throw new IllegalArgumentException( - "Start point must be less than or equal to " + "end point"); - } - mStart = start; - mEnd = end; - } - - /** Gets the start point (inclusive). */ - public int getStart() { - return mStart; - } - - /** Gets the end point (exclusive). */ - public int getEnd() { - return mEnd; - } - - @Override - public boolean equals(@Nullable Object other) { - if (this == other) { - return true; - } - if (!(other instanceof MatchRange)) { - return false; - } - MatchRange otherMatchRange = (MatchRange) other; - return this.getStart() == otherMatchRange.getStart() - && this.getEnd() == otherMatchRange.getEnd(); - } - - @Override - @NonNull - public String toString() { - return "MatchRange { start: " + mStart + " , end: " + mEnd + "}"; - } - - @Override - public int hashCode() { - return Objects.hash(mStart, mEnd); - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java deleted file mode 100644 index 4853b5b032291..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchResultPage.java +++ /dev/null @@ -1,74 +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 android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Bundle; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -/** - * This class represents a page of {@link SearchResult}s - * - * @hide - */ -public class SearchResultPage { - public static final String RESULTS_FIELD = "results"; - public static final String NEXT_PAGE_TOKEN_FIELD = "nextPageToken"; - private final long mNextPageToken; - - @Nullable private List mResults; - - @NonNull private final Bundle mBundle; - - public SearchResultPage(@NonNull Bundle bundle) { - mBundle = Objects.requireNonNull(bundle); - mNextPageToken = mBundle.getLong(NEXT_PAGE_TOKEN_FIELD); - } - - /** Returns the {@link Bundle} of this class. */ - @NonNull - public Bundle getBundle() { - return mBundle; - } - - /** Returns the Token to get next {@link SearchResultPage}. */ - public long getNextPageToken() { - return mNextPageToken; - } - - /** Returns all {@link android.app.appsearch.SearchResult}s of this page */ - @NonNull - public List getResults() { - if (mResults == null) { - ArrayList resultBundles = mBundle.getParcelableArrayList(RESULTS_FIELD); - if (resultBundles == null) { - mResults = Collections.emptyList(); - } else { - mResults = new ArrayList<>(resultBundles.size()); - for (int i = 0; i < resultBundles.size(); i++) { - mResults.add(new SearchResult(resultBundles.get(i))); - } - } - } - return mResults; - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java deleted file mode 100644 index 5abd4f69e28f6..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java +++ /dev/null @@ -1,676 +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 android.annotation.IntDef; -import android.annotation.IntRange; -import android.annotation.NonNull; -import android.annotation.SuppressLint; -import android.app.appsearch.util.BundleUtil; -import android.os.Bundle; -import android.util.ArrayMap; - -import com.android.internal.util.Preconditions; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * This class represents the specification logic for AppSearch. It can be used to set the type of - * search, like prefix or exact only or apply filters to search for a specific schema type only etc. - */ -// TODO(sidchhabra) : AddResultSpec fields for Snippets etc. -public final class SearchSpec { - /** - * Schema type to be used in {@link SearchSpec.Builder#addProjection} to apply property paths to - * all results, excepting any types that have had their own, specific property paths set. - */ - public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*"; - - static final String TERM_MATCH_TYPE_FIELD = "termMatchType"; - static final String SCHEMA_FIELD = "schema"; - static final String NAMESPACE_FIELD = "namespace"; - static final String PACKAGE_NAME_FIELD = "packageName"; - static final String NUM_PER_PAGE_FIELD = "numPerPage"; - static final String RANKING_STRATEGY_FIELD = "rankingStrategy"; - static final String ORDER_FIELD = "order"; - static final String SNIPPET_COUNT_FIELD = "snippetCount"; - static final String SNIPPET_COUNT_PER_PROPERTY_FIELD = "snippetCountPerProperty"; - static final String MAX_SNIPPET_FIELD = "maxSnippet"; - static final String PROJECTION_TYPE_PROPERTY_PATHS_FIELD = "projectionTypeFieldMasks"; - static final String RESULT_GROUPING_TYPE_FLAGS = "resultGroupingTypeFlags"; - static final String RESULT_GROUPING_LIMIT = "resultGroupingLimit"; - - /** @hide */ - public static final int DEFAULT_NUM_PER_PAGE = 10; - - // TODO(b/170371356): In framework, we may want these limits to be flag controlled. - // If that happens, the @IntRange() directives in this class may have to change. - private static final int MAX_NUM_PER_PAGE = 10_000; - private static final int MAX_SNIPPET_COUNT = 10_000; - private static final int MAX_SNIPPET_PER_PROPERTY_COUNT = 10_000; - private static final int MAX_SNIPPET_SIZE_LIMIT = 10_000; - - /** - * Term Match Type for the query. - * - * @hide - */ - // NOTE: The integer values of these constants must match the proto enum constants in - // {@link com.google.android.icing.proto.SearchSpecProto.termMatchType} - @IntDef(value = {TERM_MATCH_EXACT_ONLY, TERM_MATCH_PREFIX}) - @Retention(RetentionPolicy.SOURCE) - public @interface TermMatch {} - - /** - * Query terms will only match exact tokens in the index. - * - *

Ex. A query term "foo" will only match indexed token "foo", and not "foot" or "football". - */ - public static final int TERM_MATCH_EXACT_ONLY = 1; - /** - * Query terms will match indexed tokens when the query term is a prefix of the token. - * - *

Ex. A query term "foo" will match indexed tokens like "foo", "foot", and "football". - */ - public static final int TERM_MATCH_PREFIX = 2; - - /** - * Ranking Strategy for query result. - * - * @hide - */ - // NOTE: The integer values of these constants must match the proto enum constants in - // {@link ScoringSpecProto.RankingStrategy.Code} - @IntDef( - value = { - RANKING_STRATEGY_NONE, - RANKING_STRATEGY_DOCUMENT_SCORE, - RANKING_STRATEGY_CREATION_TIMESTAMP, - RANKING_STRATEGY_RELEVANCE_SCORE, - RANKING_STRATEGY_USAGE_COUNT, - RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP, - RANKING_STRATEGY_SYSTEM_USAGE_COUNT, - RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface RankingStrategy {} - - /** No Ranking, results are returned in arbitrary order. */ - public static final int RANKING_STRATEGY_NONE = 0; - /** Ranked by app-provided document scores. */ - public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1; - /** Ranked by document creation timestamps. */ - public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2; - /** Ranked by document relevance score. */ - public static final int RANKING_STRATEGY_RELEVANCE_SCORE = 3; - /** Ranked by number of usages, as reported by the app. */ - public static final int RANKING_STRATEGY_USAGE_COUNT = 4; - /** Ranked by timestamp of last usage, as reported by the app. */ - public static final int RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP = 5; - /** Ranked by number of usages from a system UI surface. */ - public static final int RANKING_STRATEGY_SYSTEM_USAGE_COUNT = 6; - /** Ranked by timestamp of last usage from a system UI surface. */ - public static final int RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP = 7; - - /** - * Order for query result. - * - * @hide - */ - // NOTE: The integer values of these constants must match the proto enum constants in - // {@link ScoringSpecProto.Order.Code} - @IntDef(value = {ORDER_DESCENDING, ORDER_ASCENDING}) - @Retention(RetentionPolicy.SOURCE) - public @interface Order {} - - /** Search results will be returned in a descending order. */ - public static final int ORDER_DESCENDING = 0; - /** Search results will be returned in an ascending order. */ - public static final int ORDER_ASCENDING = 1; - - /** - * Grouping type for result limits. - * - * @hide - */ - @IntDef( - flag = true, - value = {GROUPING_TYPE_PER_PACKAGE, GROUPING_TYPE_PER_NAMESPACE}) - @Retention(RetentionPolicy.SOURCE) - public @interface GroupingType {} - - /** - * Results should be grouped together by package for the purpose of enforcing a limit on the - * number of results returned per package. - */ - public static final int GROUPING_TYPE_PER_PACKAGE = 0b01; - /** - * Results should be grouped together by namespace for the purpose of enforcing a limit on the - * number of results returned per namespace. - */ - public static final int GROUPING_TYPE_PER_NAMESPACE = 0b10; - - private final Bundle mBundle; - - /** @hide */ - public SearchSpec(@NonNull Bundle bundle) { - Objects.requireNonNull(bundle); - mBundle = bundle; - } - - /** - * Returns the {@link Bundle} populated by this builder. - * - * @hide - */ - @NonNull - public Bundle getBundle() { - return mBundle; - } - - /** Returns how the query terms should match terms in the index. */ - public @TermMatch int getTermMatch() { - return mBundle.getInt(TERM_MATCH_TYPE_FIELD, -1); - } - - /** - * Returns the list of schema types to search for. - * - *

If empty, the query will search over all schema types. - */ - @NonNull - public List getFilterSchemas() { - List schemas = mBundle.getStringArrayList(SCHEMA_FIELD); - if (schemas == null) { - return Collections.emptyList(); - } - return Collections.unmodifiableList(schemas); - } - - /** - * Returns the list of namespaces to search over. - * - *

If empty, the query will search over all namespaces. - */ - @NonNull - public List getFilterNamespaces() { - List namespaces = mBundle.getStringArrayList(NAMESPACE_FIELD); - if (namespaces == null) { - return Collections.emptyList(); - } - return Collections.unmodifiableList(namespaces); - } - - /** - * Returns the list of package name filters to search over. - * - *

If empty, the query will search over all packages that the caller has access to. If - * package names are specified which caller doesn't have access to, then those package names - * will be ignored. - */ - @NonNull - public List getFilterPackageNames() { - List packageNames = mBundle.getStringArrayList(PACKAGE_NAME_FIELD); - if (packageNames == null) { - return Collections.emptyList(); - } - return Collections.unmodifiableList(packageNames); - } - - /** Returns the number of results per page in the result set. */ - public int getResultCountPerPage() { - return mBundle.getInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE); - } - - /** Returns the ranking strategy. */ - public @RankingStrategy int getRankingStrategy() { - return mBundle.getInt(RANKING_STRATEGY_FIELD); - } - - /** Returns the order of returned search results (descending or ascending). */ - public @Order int getOrder() { - return mBundle.getInt(ORDER_FIELD); - } - - /** Returns how many documents to generate snippets for. */ - public int getSnippetCount() { - return mBundle.getInt(SNIPPET_COUNT_FIELD); - } - - /** - * Returns how many matches for each property of a matching document to generate snippets for. - */ - public int getSnippetCountPerProperty() { - return mBundle.getInt(SNIPPET_COUNT_PER_PROPERTY_FIELD); - } - - /** Returns the maximum size of a snippet in characters. */ - public int getMaxSnippetSize() { - return mBundle.getInt(MAX_SNIPPET_FIELD); - } - - /** - * Returns a map from schema type to property paths to be used for projection. - * - *

If the map is empty, then all properties will be retrieved for all results. - * - *

Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this - * function, rather than calling it multiple times. - */ - @NonNull - public Map> getProjections() { - Bundle typePropertyPathsBundle = mBundle.getBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD); - Set schemas = typePropertyPathsBundle.keySet(); - Map> typePropertyPathsMap = new ArrayMap<>(schemas.size()); - for (String schema : schemas) { - typePropertyPathsMap.put(schema, typePropertyPathsBundle.getStringArrayList(schema)); - } - return typePropertyPathsMap; - } - - /** - * Get the type of grouping limit to apply, or 0 if {@link Builder#setResultGrouping} was not - * called. - */ - public @GroupingType int getResultGroupingTypeFlags() { - return mBundle.getInt(RESULT_GROUPING_TYPE_FLAGS); - } - - /** - * Get the maximum number of results to return for each group. - * - * @return the maximum number of results to return for each group or Integer.MAX_VALUE if {@link - * Builder#setResultGrouping(int, int)} was not called. - */ - public int getResultGroupingLimit() { - return mBundle.getInt(RESULT_GROUPING_LIMIT, Integer.MAX_VALUE); - } - - /** Builder for {@link SearchSpec objects}. */ - public static final class Builder { - private ArrayList mSchemas = new ArrayList<>(); - private ArrayList mNamespaces = new ArrayList<>(); - private ArrayList mPackageNames = new ArrayList<>(); - private Bundle mProjectionTypePropertyMasks = new Bundle(); - - private int mResultCountPerPage = DEFAULT_NUM_PER_PAGE; - private @TermMatch int mTermMatchType = TERM_MATCH_PREFIX; - private int mSnippetCount = 0; - private int mSnippetCountPerProperty = MAX_SNIPPET_PER_PROPERTY_COUNT; - private int mMaxSnippetSize = 0; - private @RankingStrategy int mRankingStrategy = RANKING_STRATEGY_NONE; - private @Order int mOrder = ORDER_DESCENDING; - private @GroupingType int mGroupingTypeFlags = 0; - private int mGroupingLimit = 0; - private boolean mBuilt = false; - - /** - * Indicates how the query terms should match {@code TermMatchCode} in the index. - * - *

If this method is not called, the default term match type is {@link - * SearchSpec#TERM_MATCH_PREFIX}. - */ - @NonNull - public Builder setTermMatch(@TermMatch int termMatchType) { - Preconditions.checkArgumentInRange( - termMatchType, TERM_MATCH_EXACT_ONLY, TERM_MATCH_PREFIX, "Term match type"); - resetIfBuilt(); - mTermMatchType = termMatchType; - return this; - } - - /** - * Adds a Schema type filter to {@link SearchSpec} Entry. Only search for documents that - * have the specified schema types. - * - *

If unset, the query will search over all schema types. - */ - @NonNull - public Builder addFilterSchemas(@NonNull String... schemas) { - Objects.requireNonNull(schemas); - resetIfBuilt(); - return addFilterSchemas(Arrays.asList(schemas)); - } - - /** - * Adds a Schema type filter to {@link SearchSpec} Entry. Only search for documents that - * have the specified schema types. - * - *

If unset, the query will search over all schema types. - */ - @NonNull - public Builder addFilterSchemas(@NonNull Collection schemas) { - Objects.requireNonNull(schemas); - resetIfBuilt(); - mSchemas.addAll(schemas); - return this; - } - - /** - * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that have - * the specified namespaces. - * - *

If unset, the query will search over all namespaces. - */ - @NonNull - public Builder addFilterNamespaces(@NonNull String... namespaces) { - Objects.requireNonNull(namespaces); - resetIfBuilt(); - return addFilterNamespaces(Arrays.asList(namespaces)); - } - - /** - * Adds a namespace filter to {@link SearchSpec} Entry. Only search for documents that have - * the specified namespaces. - * - *

If unset, the query will search over all namespaces. - */ - @NonNull - public Builder addFilterNamespaces(@NonNull Collection namespaces) { - Objects.requireNonNull(namespaces); - resetIfBuilt(); - mNamespaces.addAll(namespaces); - return this; - } - - /** - * Adds a package name filter to {@link SearchSpec} Entry. Only search for documents that - * were indexed from the specified packages. - * - *

If unset, the query will search over all packages that the caller has access to. If - * package names are specified which caller doesn't have access to, then those package names - * will be ignored. - */ - @NonNull - public Builder addFilterPackageNames(@NonNull String... packageNames) { - Objects.requireNonNull(packageNames); - resetIfBuilt(); - return addFilterPackageNames(Arrays.asList(packageNames)); - } - - /** - * Adds a package name filter to {@link SearchSpec} Entry. Only search for documents that - * were indexed from the specified packages. - * - *

If unset, the query will search over all packages that the caller has access to. If - * package names are specified which caller doesn't have access to, then those package names - * will be ignored. - */ - @NonNull - public Builder addFilterPackageNames(@NonNull Collection packageNames) { - Objects.requireNonNull(packageNames); - resetIfBuilt(); - mPackageNames.addAll(packageNames); - return this; - } - - /** - * Sets the number of results per page in the returned object. - * - *

The default number of results per page is 10. - */ - @NonNull - public SearchSpec.Builder setResultCountPerPage( - @IntRange(from = 0, to = MAX_NUM_PER_PAGE) int resultCountPerPage) { - Preconditions.checkArgumentInRange( - resultCountPerPage, 0, MAX_NUM_PER_PAGE, "resultCountPerPage"); - resetIfBuilt(); - mResultCountPerPage = resultCountPerPage; - return this; - } - - /** Sets ranking strategy for AppSearch results. */ - @NonNull - public Builder setRankingStrategy(@RankingStrategy int rankingStrategy) { - Preconditions.checkArgumentInRange( - rankingStrategy, - RANKING_STRATEGY_NONE, - RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP, - "Result ranking strategy"); - resetIfBuilt(); - mRankingStrategy = rankingStrategy; - return this; - } - - /** - * Indicates the order of returned search results, the default is {@link #ORDER_DESCENDING}, - * meaning that results with higher scores come first. - * - *

This order field will be ignored if RankingStrategy = {@code RANKING_STRATEGY_NONE}. - */ - @NonNull - public Builder setOrder(@Order int order) { - Preconditions.checkArgumentInRange( - order, ORDER_DESCENDING, ORDER_ASCENDING, "Result ranking order"); - resetIfBuilt(); - mOrder = order; - return this; - } - - /** - * Only the first {@code snippetCount} documents based on the ranking strategy will have - * snippet information provided. - * - *

The list returned from {@link SearchResult#getMatchInfos} will contain at most this - * many entries. - * - *

If set to 0 (default), snippeting is disabled and the list returned from {@link - * SearchResult#getMatchInfos} will be empty. - */ - @NonNull - public SearchSpec.Builder setSnippetCount( - @IntRange(from = 0, to = MAX_SNIPPET_COUNT) int snippetCount) { - Preconditions.checkArgumentInRange(snippetCount, 0, MAX_SNIPPET_COUNT, "snippetCount"); - resetIfBuilt(); - mSnippetCount = snippetCount; - return this; - } - - /** - * Sets {@code snippetCountPerProperty}. Only the first {@code snippetCountPerProperty} - * snippets for each property of each {@link GenericDocument} will contain snippet - * information. - * - *

If set to 0, snippeting is disabled and the list returned from {@link - * SearchResult#getMatchInfos} will be empty. - * - *

The default behavior is to snippet all matches a property contains, up to the maximum - * value of 10,000. - */ - @NonNull - public SearchSpec.Builder setSnippetCountPerProperty( - @IntRange(from = 0, to = MAX_SNIPPET_PER_PROPERTY_COUNT) - int snippetCountPerProperty) { - Preconditions.checkArgumentInRange( - snippetCountPerProperty, - 0, - MAX_SNIPPET_PER_PROPERTY_COUNT, - "snippetCountPerProperty"); - resetIfBuilt(); - mSnippetCountPerProperty = snippetCountPerProperty; - return this; - } - - /** - * Sets {@code maxSnippetSize}, the maximum snippet size. Snippet windows start at {@code - * maxSnippetSize/2} bytes before the middle of the matching token and end at {@code - * maxSnippetSize/2} bytes after the middle of the matching token. It respects token - * boundaries, therefore the returned window may be smaller than requested. - * - *

Setting {@code maxSnippetSize} to 0 will disable windowing and an empty string will be - * returned. If matches enabled is also set to false, then snippeting is disabled. - * - *

Ex. {@code maxSnippetSize} = 16. "foo bar baz bat rat" with a query of "baz" will - * return a window of "bar baz bat" which is only 11 bytes long. - */ - @NonNull - public SearchSpec.Builder setMaxSnippetSize( - @IntRange(from = 0, to = MAX_SNIPPET_SIZE_LIMIT) int maxSnippetSize) { - Preconditions.checkArgumentInRange( - maxSnippetSize, 0, MAX_SNIPPET_SIZE_LIMIT, "maxSnippetSize"); - resetIfBuilt(); - mMaxSnippetSize = maxSnippetSize; - return this; - } - - /** - * Adds property paths for the specified type to be used for projection. If property paths - * are added for a type, then only the properties referred to will be retrieved for results - * of that type. If a property path that is specified isn't present in a result, it will be - * ignored for that result. Property paths cannot be null. - * - *

If no property paths are added for a particular type, then all properties of results - * of that type will be retrieved. - * - *

If property path is added for the {@link SearchSpec#PROJECTION_SCHEMA_TYPE_WILDCARD}, - * then those property paths will apply to all results, excepting any types that have their - * own, specific property paths set. - * - *

Suppose the following document is in the index. - * - *

{@code
-         * Email: Document {
-         *   sender: Document {
-         *     name: "Mr. Person"
-         *     email: "mrperson123@google.com"
-         *   }
-         *   recipients: [
-         *     Document {
-         *       name: "John Doe"
-         *       email: "johndoe123@google.com"
-         *     }
-         *     Document {
-         *       name: "Jane Doe"
-         *       email: "janedoe123@google.com"
-         *     }
-         *   ]
-         *   subject: "IMPORTANT"
-         *   body: "Limited time offer!"
-         * }
-         * }
- * - *

Then, suppose that a query for "important" is issued with the following projection - * type property paths: - * - *

{@code
-         * {schema: "Email", ["subject", "sender.name", "recipients.name"]}
-         * }
- * - *

The above document will be returned as: - * - *

{@code
-         * Email: Document {
-         *   sender: Document {
-         *     name: "Mr. Body"
-         *   }
-         *   recipients: [
-         *     Document {
-         *       name: "John Doe"
-         *     }
-         *     Document {
-         *       name: "Jane Doe"
-         *     }
-         *   ]
-         *   subject: "IMPORTANT"
-         * }
-         * }
- */ - @NonNull - public SearchSpec.Builder addProjection( - @NonNull String schema, @NonNull Collection propertyPaths) { - Objects.requireNonNull(schema); - Objects.requireNonNull(propertyPaths); - resetIfBuilt(); - ArrayList propertyPathsArrayList = new ArrayList<>(propertyPaths.size()); - for (String propertyPath : propertyPaths) { - Objects.requireNonNull(propertyPath); - propertyPathsArrayList.add(propertyPath); - } - mProjectionTypePropertyMasks.putStringArrayList(schema, propertyPathsArrayList); - return this; - } - - /** - * Set the maximum number of results to return for each group, where groups are defined by - * grouping type. - * - *

Calling this method will override any previous calls. So calling - * setResultGrouping(GROUPING_TYPE_PER_PACKAGE, 7) and then calling - * setResultGrouping(GROUPING_TYPE_PER_PACKAGE, 2) will result in only the latter, a limit - * of two results per package, being applied. Or calling setResultGrouping - * (GROUPING_TYPE_PER_PACKAGE, 1) and then calling setResultGrouping - * (GROUPING_TYPE_PER_PACKAGE | GROUPING_PER_NAMESPACE, 5) will result in five results per - * package per namespace. - * - * @param groupingTypeFlags One or more combination of grouping types. - * @param limit Number of results to return per {@code groupingTypeFlags}. - * @throws IllegalArgumentException if groupingTypeFlags is zero. - */ - // Individual parameters available from getResultGroupingTypeFlags and - // getResultGroupingLimit - @SuppressLint("MissingGetterMatchingBuilder") - @NonNull - public Builder setResultGrouping(@GroupingType int groupingTypeFlags, int limit) { - Preconditions.checkState( - groupingTypeFlags != 0, "Result grouping type cannot be zero."); - resetIfBuilt(); - mGroupingTypeFlags = groupingTypeFlags; - mGroupingLimit = limit; - return this; - } - - /** Constructs a new {@link SearchSpec} from the contents of this builder. */ - @NonNull - public SearchSpec build() { - Bundle bundle = new Bundle(); - bundle.putStringArrayList(SCHEMA_FIELD, mSchemas); - bundle.putStringArrayList(NAMESPACE_FIELD, mNamespaces); - bundle.putStringArrayList(PACKAGE_NAME_FIELD, mPackageNames); - bundle.putBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD, mProjectionTypePropertyMasks); - bundle.putInt(NUM_PER_PAGE_FIELD, mResultCountPerPage); - bundle.putInt(TERM_MATCH_TYPE_FIELD, mTermMatchType); - bundle.putInt(SNIPPET_COUNT_FIELD, mSnippetCount); - bundle.putInt(SNIPPET_COUNT_PER_PROPERTY_FIELD, mSnippetCountPerProperty); - bundle.putInt(MAX_SNIPPET_FIELD, mMaxSnippetSize); - bundle.putInt(RANKING_STRATEGY_FIELD, mRankingStrategy); - bundle.putInt(ORDER_FIELD, mOrder); - bundle.putInt(RESULT_GROUPING_TYPE_FLAGS, mGroupingTypeFlags); - bundle.putInt(RESULT_GROUPING_LIMIT, mGroupingLimit); - mBuilt = true; - return new SearchSpec(bundle); - } - - private void resetIfBuilt() { - if (mBuilt) { - mSchemas = new ArrayList<>(mSchemas); - mNamespaces = new ArrayList<>(mNamespaces); - mPackageNames = new ArrayList<>(mPackageNames); - mProjectionTypePropertyMasks = BundleUtil.deepCopy(mProjectionTypePropertyMasks); - mBuilt = false; - } - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java deleted file mode 100644 index b72ca9af42cb9..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaRequest.java +++ /dev/null @@ -1,461 +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 android.annotation.IntRange; -import android.annotation.NonNull; -import android.annotation.SuppressLint; -import android.util.ArrayMap; -import android.util.ArraySet; - -import com.android.internal.util.Preconditions; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * Encapsulates a request to update the schema of an {@link AppSearchSession} database. - * - *

The schema is composed of a collection of {@link AppSearchSchema} objects, each of which - * defines a unique type of data. - * - *

The first call to SetSchemaRequest will set the provided schema and store it within the {@link - * AppSearchSession} database. - * - *

Subsequent calls will compare the provided schema to the previously saved schema, to determine - * how to treat existing documents. - * - *

The following types of schema modifications are always safe and are made without deleting any - * existing documents: - * - *

    - *
  • Addition of new {@link AppSearchSchema} types - *
  • Addition of new properties to an existing {@link AppSearchSchema} type - *
  • Changing the cardinality of a property to be less restrictive - *
- * - *

The following types of schema changes are not backwards compatible: - * - *

    - *
  • Removal of an existing {@link AppSearchSchema} type - *
  • Removal of a property from an existing {@link AppSearchSchema} type - *
  • Changing the data type of an existing property - *
  • Changing the cardinality of a property to be more restrictive - *
- * - *

Providing a schema with incompatible changes, will throw an {@link - * android.app.appsearch.exceptions.AppSearchException}, with a message describing the - * incompatibility. As a result, the previously set schema will remain unchanged. - * - *

Backward incompatible changes can be made by : - * - *

    - *
  • setting {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. This - * deletes all documents that are incompatible with the new schema. The new schema is then - * saved and persisted to disk. - *
  • Add a {@link Migrator} for each incompatible type and make no deletion. The migrator will - * migrate documents from it's old schema version to the new version. Migrated types will be - * set into both {@link SetSchemaResponse#getIncompatibleTypes()} and {@link - * SetSchemaResponse#getMigratedTypes()}. See the migration section below. - *
- * - * @see AppSearchSession#setSchema - * @see Migrator - */ -public final class SetSchemaRequest { - private final Set mSchemas; - private final Set mSchemasNotDisplayedBySystem; - private final Map> mSchemasVisibleToPackages; - private final Map mMigrators; - private final boolean mForceOverride; - private final int mVersion; - - SetSchemaRequest( - @NonNull Set schemas, - @NonNull Set schemasNotDisplayedBySystem, - @NonNull Map> schemasVisibleToPackages, - @NonNull Map migrators, - boolean forceOverride, - int version) { - mSchemas = Objects.requireNonNull(schemas); - mSchemasNotDisplayedBySystem = Objects.requireNonNull(schemasNotDisplayedBySystem); - mSchemasVisibleToPackages = Objects.requireNonNull(schemasVisibleToPackages); - mMigrators = Objects.requireNonNull(migrators); - mForceOverride = forceOverride; - mVersion = version; - } - - /** Returns the {@link AppSearchSchema} types that are part of this request. */ - @NonNull - public Set getSchemas() { - return Collections.unmodifiableSet(mSchemas); - } - - /** - * Returns all the schema types that are opted out of being displayed and visible on any system - * UI surface. - */ - @NonNull - public Set getSchemasNotDisplayedBySystem() { - return Collections.unmodifiableSet(mSchemasNotDisplayedBySystem); - } - - /** - * Returns a mapping of schema types to the set of packages that have access to that schema - * type. - * - *

It’s inefficient to call this method repeatedly. - */ - @NonNull - public Map> getSchemasVisibleToPackages() { - Map> copy = new ArrayMap<>(); - for (String key : mSchemasVisibleToPackages.keySet()) { - copy.put(key, new ArraySet<>(mSchemasVisibleToPackages.get(key))); - } - return copy; - } - - /** - * Returns the map of {@link Migrator}, the key will be the schema type of the {@link Migrator} - * associated with. - */ - @NonNull - public Map getMigrators() { - return Collections.unmodifiableMap(mMigrators); - } - - /** - * Returns a mapping of {@link AppSearchSchema} types to the set of packages that have access to - * that schema type. - * - *

A more efficient version of {@link #getSchemasVisibleToPackages}, but it returns a - * modifiable map. This is not meant to be unhidden and should only be used by internal classes. - * - * @hide - */ - @NonNull - public Map> getSchemasVisibleToPackagesInternal() { - return mSchemasVisibleToPackages; - } - - /** Returns whether this request will force the schema to be overridden. */ - public boolean isForceOverride() { - return mForceOverride; - } - - /** Returns the database overall schema version. */ - @IntRange(from = 1) - public int getVersion() { - return mVersion; - } - - /** Builder for {@link SetSchemaRequest} objects. */ - public static final class Builder { - private static final int DEFAULT_VERSION = 1; - private ArraySet mSchemas = new ArraySet<>(); - private ArraySet mSchemasNotDisplayedBySystem = new ArraySet<>(); - private ArrayMap> mSchemasVisibleToPackages = - new ArrayMap<>(); - private ArrayMap mMigrators = new ArrayMap<>(); - private boolean mForceOverride = false; - private int mVersion = DEFAULT_VERSION; - private boolean mBuilt = false; - - /** - * Adds one or more {@link AppSearchSchema} types to the schema. - * - *

An {@link AppSearchSchema} object represents one type of structured data. - * - *

Any documents of these types will be displayed on system UI surfaces by default. - */ - @NonNull - public Builder addSchemas(@NonNull AppSearchSchema... schemas) { - Objects.requireNonNull(schemas); - resetIfBuilt(); - return addSchemas(Arrays.asList(schemas)); - } - - /** - * Adds a collection of {@link AppSearchSchema} objects to the schema. - * - *

An {@link AppSearchSchema} object represents one type of structured data. - */ - @NonNull - public Builder addSchemas(@NonNull Collection schemas) { - Objects.requireNonNull(schemas); - resetIfBuilt(); - mSchemas.addAll(schemas); - return this; - } - - /** - * Sets whether or not documents from the provided {@code schemaType} will be displayed and - * visible on any system UI surface. - * - *

This setting applies to the provided {@code schemaType} only, and does not persist - * across {@link AppSearchSession#setSchema} calls. - * - *

The default behavior, if this method is not called, is to allow types to be displayed - * on system UI surfaces. - * - * @param schemaType The name of an {@link AppSearchSchema} within the same {@link - * SetSchemaRequest}, which will be configured. - * @param displayed Whether documents of this type will be displayed on system UI surfaces. - */ - // Merged list available from getSchemasNotDisplayedBySystem - @SuppressLint("MissingGetterMatchingBuilder") - @NonNull - public Builder setSchemaTypeDisplayedBySystem( - @NonNull String schemaType, boolean displayed) { - Objects.requireNonNull(schemaType); - resetIfBuilt(); - if (displayed) { - mSchemasNotDisplayedBySystem.remove(schemaType); - } else { - mSchemasNotDisplayedBySystem.add(schemaType); - } - return this; - } - - /** - * Sets whether or not documents from the provided {@code schemaType} can be read by the - * specified package. - * - *

Each package is represented by a {@link PackageIdentifier}, containing a package name - * and a byte array of type {@link android.content.pm.PackageManager#CERT_INPUT_SHA256}. - * - *

To opt into one-way data sharing with another application, the developer will need to - * explicitly grant the other application’s package name and certificate Read access to its - * data. - * - *

For two-way data sharing, both applications need to explicitly grant Read access to - * one another. - * - *

By default, data sharing between applications is disabled. - * - * @param schemaType The schema type to set visibility on. - * @param visible Whether the {@code schemaType} will be visible or not. - * @param packageIdentifier Represents the package that will be granted visibility. - */ - // Merged list available from getSchemasVisibleToPackages - @SuppressLint("MissingGetterMatchingBuilder") - @NonNull - public Builder setSchemaTypeVisibilityForPackage( - @NonNull String schemaType, - boolean visible, - @NonNull PackageIdentifier packageIdentifier) { - Objects.requireNonNull(schemaType); - Objects.requireNonNull(packageIdentifier); - resetIfBuilt(); - - Set packageIdentifiers = mSchemasVisibleToPackages.get(schemaType); - if (visible) { - if (packageIdentifiers == null) { - packageIdentifiers = new ArraySet<>(); - } - packageIdentifiers.add(packageIdentifier); - mSchemasVisibleToPackages.put(schemaType, packageIdentifiers); - } else { - if (packageIdentifiers == null) { - // Return early since there was nothing set to begin with. - return this; - } - packageIdentifiers.remove(packageIdentifier); - if (packageIdentifiers.isEmpty()) { - // Remove the entire key so that we don't have empty sets as values. - mSchemasVisibleToPackages.remove(schemaType); - } - } - - return this; - } - - /** - * Sets the {@link Migrator} associated with the given SchemaType. - * - *

The {@link Migrator} migrates all {@link GenericDocument}s under given schema type - * from the current version number stored in AppSearch to the final version set via {@link - * #setVersion}. - * - *

A {@link Migrator} will be invoked if the current version number stored in AppSearch - * is different from the final version set via {@link #setVersion} and {@link - * Migrator#shouldMigrate} returns {@code true}. - * - *

The target schema type of the output {@link GenericDocument} of {@link - * Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this {@link - * SetSchemaRequest}. - * - * @param schemaType The schema type to set migrator on. - * @param migrator The migrator translates a document from its current version to the final - * version set via {@link #setVersion}. - * @see SetSchemaRequest.Builder#setVersion - * @see SetSchemaRequest.Builder#addSchemas - * @see AppSearchSession#setSchema - */ - @NonNull - @SuppressLint("MissingGetterMatchingBuilder") // Getter return plural objects. - public Builder setMigrator(@NonNull String schemaType, @NonNull Migrator migrator) { - Objects.requireNonNull(schemaType); - Objects.requireNonNull(migrator); - resetIfBuilt(); - mMigrators.put(schemaType, migrator); - return this; - } - - /** - * Sets a Map of {@link Migrator}s. - * - *

The key of the map is the schema type that the {@link Migrator} value applies to. - * - *

The {@link Migrator} migrates all {@link GenericDocument}s under given schema type - * from the current version number stored in AppSearch to the final version set via {@link - * #setVersion}. - * - *

A {@link Migrator} will be invoked if the current version number stored in AppSearch - * is different from the final version set via {@link #setVersion} and {@link - * Migrator#shouldMigrate} returns {@code true}. - * - *

The target schema type of the output {@link GenericDocument} of {@link - * Migrator#onUpgrade} or {@link Migrator#onDowngrade} must exist in this {@link - * SetSchemaRequest}. - * - * @param migrators A {@link Map} of migrators that translate a document from it's current - * version to the final version set via {@link #setVersion}. The key of the map is the - * schema type that the {@link Migrator} value applies to. - * @see SetSchemaRequest.Builder#setVersion - * @see SetSchemaRequest.Builder#addSchemas - * @see AppSearchSession#setSchema - */ - @NonNull - public Builder setMigrators(@NonNull Map migrators) { - Objects.requireNonNull(migrators); - resetIfBuilt(); - mMigrators.putAll(migrators); - return this; - } - - /** - * Sets whether or not to override the current schema in the {@link AppSearchSession} - * database. - * - *

Call this method whenever backward incompatible changes need to be made by setting - * {@code forceOverride} to {@code true}. As a result, during execution of the setSchema - * operation, all documents that are incompatible with the new schema will be deleted and - * the new schema will be saved and persisted. - * - *

By default, this is {@code false}. - */ - @NonNull - public Builder setForceOverride(boolean forceOverride) { - resetIfBuilt(); - mForceOverride = forceOverride; - return this; - } - - /** - * Sets the version number of the overall {@link AppSearchSchema} in the database. - * - *

The {@link AppSearchSession} database can only ever hold documents for one version at - * a time. - * - *

Setting a version number that is different from the version number currently stored in - * AppSearch will result in AppSearch calling the {@link Migrator}s provided to {@link - * AppSearchSession#setSchema} to migrate the documents already in AppSearch from the - * previous version to the one set in this request. The version number can be updated - * without any other changes to the set of schemas. - * - *

The version number can stay the same, increase, or decrease relative to the current - * version number that is already stored in the {@link AppSearchSession} database. - * - *

The version of an empty database will always be 0. You cannot set version to the - * {@link SetSchemaRequest}, if it doesn't contains any {@link AppSearchSchema}. - * - * @param version A positive integer representing the version of the entire set of schemas - * represents the version of the whole schema in the {@link AppSearchSession} database, - * default version is 1. - * @throws IllegalArgumentException if the version is negative. - * @see AppSearchSession#setSchema - * @see Migrator - * @see SetSchemaRequest.Builder#setMigrator - */ - @NonNull - public Builder setVersion(@IntRange(from = 1) int version) { - Preconditions.checkArgument(version >= 1, "Version must be a positive number."); - resetIfBuilt(); - mVersion = version; - return this; - } - - /** - * Builds a new {@link SetSchemaRequest} object. - * - * @throws IllegalArgumentException if schema types were referenced, but the corresponding - * {@link AppSearchSchema} type was never added. - */ - @NonNull - public SetSchemaRequest build() { - // Verify that any schema types with display or visibility settings refer to a real - // schema. - // Create a copy because we're going to remove from the set for verification purposes. - Set referencedSchemas = new ArraySet<>(mSchemasNotDisplayedBySystem); - referencedSchemas.addAll(mSchemasVisibleToPackages.keySet()); - - for (AppSearchSchema schema : mSchemas) { - referencedSchemas.remove(schema.getSchemaType()); - } - if (!referencedSchemas.isEmpty()) { - // We still have schema types that weren't seen in our mSchemas set. This means - // there wasn't a corresponding AppSearchSchema. - throw new IllegalArgumentException( - "Schema types " + referencedSchemas + " referenced, but were not added."); - } - if (mSchemas.isEmpty() && mVersion != DEFAULT_VERSION) { - throw new IllegalArgumentException( - "Cannot set version to the request if schema is empty."); - } - mBuilt = true; - return new SetSchemaRequest( - mSchemas, - mSchemasNotDisplayedBySystem, - mSchemasVisibleToPackages, - mMigrators, - mForceOverride, - mVersion); - } - - private void resetIfBuilt() { - if (mBuilt) { - ArrayMap> schemasVisibleToPackages = - new ArrayMap<>(mSchemasVisibleToPackages.size()); - for (Map.Entry> entry : - mSchemasVisibleToPackages.entrySet()) { - schemasVisibleToPackages.put(entry.getKey(), new ArraySet<>(entry.getValue())); - } - mSchemasVisibleToPackages = schemasVisibleToPackages; - - mSchemas = new ArraySet<>(mSchemas); - mSchemasNotDisplayedBySystem = new ArraySet<>(mSchemasNotDisplayedBySystem); - mMigrators = new ArrayMap<>(mMigrators); - mBuilt = false; - } - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java b/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java deleted file mode 100644 index a3a4a23e577bb..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/SetSchemaResponse.java +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Bundle; -import android.util.ArraySet; - -import com.android.internal.util.Preconditions; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Set; - -/** The response class of {@link AppSearchSession#setSchema} */ -public class SetSchemaResponse { - - private static final String DELETED_TYPES_FIELD = "deletedTypes"; - private static final String INCOMPATIBLE_TYPES_FIELD = "incompatibleTypes"; - private static final String MIGRATED_TYPES_FIELD = "migratedTypes"; - - private final Bundle mBundle; - /** - * The migrationFailures won't be saved in the bundle. Since: - * - *

    - *
  • {@link MigrationFailure} is generated in {@link AppSearchSession} which will be the SDK - * side in platform. We don't need to pass it from service side via binder. - *
  • Translate multiple {@link MigrationFailure}s to bundles in {@link Builder} and then - * back in constructor will be a huge waste. - *
- */ - private final List mMigrationFailures; - - /** Cache of the inflated deleted schema types. Comes from inflating mBundles at first use. */ - @Nullable private Set mDeletedTypes; - - /** Cache of the inflated migrated schema types. Comes from inflating mBundles at first use. */ - @Nullable private Set mMigratedTypes; - - /** - * Cache of the inflated incompatible schema types. Comes from inflating mBundles at first use. - */ - @Nullable private Set mIncompatibleTypes; - - SetSchemaResponse(@NonNull Bundle bundle, @NonNull List migrationFailures) { - mBundle = Objects.requireNonNull(bundle); - mMigrationFailures = Objects.requireNonNull(migrationFailures); - } - - SetSchemaResponse(@NonNull Bundle bundle) { - this(bundle, /*migrationFailures=*/ Collections.emptyList()); - } - - /** - * Returns the {@link Bundle} populated by this builder. - * - * @hide - */ - @NonNull - public Bundle getBundle() { - return mBundle; - } - - /** - * Returns a {@link List} of all failed {@link MigrationFailure}. - * - *

A {@link MigrationFailure} will be generated if the system trying to save a post-migrated - * {@link GenericDocument} but fail. - * - *

{@link MigrationFailure} contains the namespace, id and schemaType of the post-migrated - * {@link GenericDocument} and the error reason. Mostly it will be mismatch the schema it - * migrated to. - */ - @NonNull - public List getMigrationFailures() { - return Collections.unmodifiableList(mMigrationFailures); - } - - /** - * Returns a {@link Set} of deleted schema types. - * - *

A "deleted" type is a schema type that was previously a part of the database schema but - * was not present in the {@link SetSchemaRequest} object provided in the - * {@link AppSearchSession#setSchema) call. - * - *

Documents for a deleted type are removed from the database. - */ - @NonNull - public Set getDeletedTypes() { - if (mDeletedTypes == null) { - mDeletedTypes = - new ArraySet<>( - Objects.requireNonNull( - mBundle.getStringArrayList(DELETED_TYPES_FIELD))); - } - return Collections.unmodifiableSet(mDeletedTypes); - } - - /** - * Returns a {@link Set} of schema type that were migrated by the {@link - * AppSearchSession#setSchema} call. - * - *

A "migrated" type is a schema type that has triggered a {@link Migrator} instance to - * migrate documents of the schema type to another schema type, or to another version of the - * schema type. - * - *

If a document fails to be migrated, a {@link MigrationFailure} will be generated for that - * document. - * - * @see Migrator - */ - @NonNull - public Set getMigratedTypes() { - if (mMigratedTypes == null) { - mMigratedTypes = - new ArraySet<>( - Objects.requireNonNull( - mBundle.getStringArrayList(MIGRATED_TYPES_FIELD))); - } - return Collections.unmodifiableSet(mMigratedTypes); - } - - /** - * Returns a {@link Set} of schema type whose new definitions set in the {@link - * AppSearchSession#setSchema} call were incompatible with the pre-existing schema. - * - *

If a {@link Migrator} is provided for this type and the migration is success triggered. - * The type will also appear in {@link #getMigratedTypes()}. - * - * @see SetSchemaRequest - * @see AppSearchSession#setSchema - * @see SetSchemaRequest.Builder#setForceOverride - */ - @NonNull - public Set getIncompatibleTypes() { - if (mIncompatibleTypes == null) { - mIncompatibleTypes = - new ArraySet<>( - Objects.requireNonNull( - mBundle.getStringArrayList(INCOMPATIBLE_TYPES_FIELD))); - } - return Collections.unmodifiableSet(mIncompatibleTypes); - } - - /** - * Translates the {@link SetSchemaResponse}'s bundle to {@link Builder}. - * - * @hide - */ - @NonNull - // TODO(b/179302942) change to Builder(mBundle) powered by mBundle.deepCopy - public Builder toBuilder() { - return new Builder() - .addDeletedTypes(getDeletedTypes()) - .addIncompatibleTypes(getIncompatibleTypes()) - .addMigratedTypes(getMigratedTypes()) - .addMigrationFailures(mMigrationFailures); - } - - /** Builder for {@link SetSchemaResponse} objects. */ - public static final class Builder { - private List mMigrationFailures = new ArrayList<>(); - private ArrayList mDeletedTypes = new ArrayList<>(); - private ArrayList mMigratedTypes = new ArrayList<>(); - private ArrayList mIncompatibleTypes = new ArrayList<>(); - private boolean mBuilt = false; - - /** Adds {@link MigrationFailure}s to the list of migration failures. */ - @NonNull - public Builder addMigrationFailures( - @NonNull Collection migrationFailures) { - Objects.requireNonNull(migrationFailures); - resetIfBuilt(); - mMigrationFailures.addAll(migrationFailures); - return this; - } - - /** Adds a {@link MigrationFailure} to the list of migration failures. */ - @NonNull - public Builder addMigrationFailure(@NonNull MigrationFailure migrationFailure) { - Objects.requireNonNull(migrationFailure); - resetIfBuilt(); - mMigrationFailures.add(migrationFailure); - return this; - } - - /** Adds deletedTypes to the list of deleted schema types. */ - @NonNull - public Builder addDeletedTypes(@NonNull Collection deletedTypes) { - Objects.requireNonNull(deletedTypes); - resetIfBuilt(); - mDeletedTypes.addAll(deletedTypes); - return this; - } - - /** Adds one deletedType to the list of deleted schema types. */ - @NonNull - public Builder addDeletedType(@NonNull String deletedType) { - Objects.requireNonNull(deletedType); - resetIfBuilt(); - mDeletedTypes.add(deletedType); - return this; - } - - /** Adds incompatibleTypes to the list of incompatible schema types. */ - @NonNull - public Builder addIncompatibleTypes(@NonNull Collection incompatibleTypes) { - Objects.requireNonNull(incompatibleTypes); - resetIfBuilt(); - mIncompatibleTypes.addAll(incompatibleTypes); - return this; - } - - /** Adds one incompatibleType to the list of incompatible schema types. */ - @NonNull - public Builder addIncompatibleType(@NonNull String incompatibleType) { - Objects.requireNonNull(incompatibleType); - resetIfBuilt(); - mIncompatibleTypes.add(incompatibleType); - return this; - } - - /** Adds migratedTypes to the list of migrated schema types. */ - @NonNull - public Builder addMigratedTypes(@NonNull Collection migratedTypes) { - Objects.requireNonNull(migratedTypes); - resetIfBuilt(); - mMigratedTypes.addAll(migratedTypes); - return this; - } - - /** Adds one migratedType to the list of migrated schema types. */ - @NonNull - public Builder addMigratedType(@NonNull String migratedType) { - Objects.requireNonNull(migratedType); - resetIfBuilt(); - mMigratedTypes.add(migratedType); - return this; - } - - /** Builds a {@link SetSchemaResponse} object. */ - @NonNull - public SetSchemaResponse build() { - Bundle bundle = new Bundle(); - bundle.putStringArrayList(INCOMPATIBLE_TYPES_FIELD, mIncompatibleTypes); - bundle.putStringArrayList(DELETED_TYPES_FIELD, mDeletedTypes); - bundle.putStringArrayList(MIGRATED_TYPES_FIELD, mMigratedTypes); - mBuilt = true; - // Avoid converting the potential thousands of MigrationFailures to Pracelable and - // back just for put in bundle. In platform, we should set MigrationFailures in - // AppSearchSession after we pass SetSchemaResponse via binder. - return new SetSchemaResponse(bundle, mMigrationFailures); - } - - private void resetIfBuilt() { - if (mBuilt) { - mMigrationFailures = new ArrayList<>(mMigrationFailures); - mDeletedTypes = new ArrayList<>(mDeletedTypes); - mMigratedTypes = new ArrayList<>(mMigratedTypes); - mIncompatibleTypes = new ArrayList<>(mIncompatibleTypes); - mBuilt = false; - } - } - } - - /** - * The class represents a post-migrated {@link GenericDocument} that failed to be saved by - * {@link AppSearchSession#setSchema}. - */ - public static class MigrationFailure { - private static final String SCHEMA_TYPE_FIELD = "schemaType"; - private static final String NAMESPACE_FIELD = "namespace"; - private static final String DOCUMENT_ID_FIELD = "id"; - private static final String ERROR_MESSAGE_FIELD = "errorMessage"; - private static final String RESULT_CODE_FIELD = "resultCode"; - - private final Bundle mBundle; - - /** - * Constructs a new {@link MigrationFailure}. - * - * @param namespace The namespace of the document which failed to be migrated. - * @param documentId The id of the document which failed to be migrated. - * @param schemaType The type of the document which failed to be migrated. - * @param failedResult The reason why the document failed to be indexed. - * @throws IllegalArgumentException if the provided {@code failedResult} was not a failure. - */ - public MigrationFailure( - @NonNull String namespace, - @NonNull String documentId, - @NonNull String schemaType, - @NonNull AppSearchResult failedResult) { - mBundle = new Bundle(); - mBundle.putString(NAMESPACE_FIELD, Objects.requireNonNull(namespace)); - mBundle.putString(DOCUMENT_ID_FIELD, Objects.requireNonNull(documentId)); - mBundle.putString(SCHEMA_TYPE_FIELD, Objects.requireNonNull(schemaType)); - - Objects.requireNonNull(failedResult); - Preconditions.checkArgument( - !failedResult.isSuccess(), "failedResult was actually successful"); - mBundle.putString(ERROR_MESSAGE_FIELD, failedResult.getErrorMessage()); - mBundle.putInt(RESULT_CODE_FIELD, failedResult.getResultCode()); - } - - MigrationFailure(@NonNull Bundle bundle) { - mBundle = Objects.requireNonNull(bundle); - } - - /** - * Returns the Bundle of the {@link MigrationFailure}. - * - * @hide - */ - @NonNull - public Bundle getBundle() { - return mBundle; - } - - /** Returns the namespace of the {@link GenericDocument} that failed to be migrated. */ - @NonNull - public String getNamespace() { - return mBundle.getString(NAMESPACE_FIELD, /*defaultValue=*/ ""); - } - - /** Returns the id of the {@link GenericDocument} that failed to be migrated. */ - @NonNull - public String getDocumentId() { - return mBundle.getString(DOCUMENT_ID_FIELD, /*defaultValue=*/ ""); - } - - /** Returns the schema type of the {@link GenericDocument} that failed to be migrated. */ - @NonNull - public String getSchemaType() { - return mBundle.getString(SCHEMA_TYPE_FIELD, /*defaultValue=*/ ""); - } - - /** - * Returns the {@link AppSearchResult} that indicates why the post-migration {@link - * GenericDocument} failed to be indexed. - */ - @NonNull - public AppSearchResult getAppSearchResult() { - return AppSearchResult.newFailedResult( - mBundle.getInt(RESULT_CODE_FIELD), - mBundle.getString(ERROR_MESSAGE_FIELD, /*defaultValue=*/ "")); - } - - @NonNull - @Override - public String toString() { - return "MigrationFailure { schemaType: " - + getSchemaType() - + ", namespace: " - + getNamespace() - + ", documentId: " - + getDocumentId() - + ", appSearchResult: " - + getAppSearchResult().toString() - + "}"; - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/StorageInfo.java b/apex/appsearch/framework/java/external/android/app/appsearch/StorageInfo.java deleted file mode 100644 index 64d48281e6f83..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/StorageInfo.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch; - -import android.annotation.NonNull; -import android.os.Bundle; - -import java.util.Objects; - -/** The response class of {@code AppSearchSession#getStorageInfo}. */ -public class StorageInfo { - - private static final String SIZE_BYTES_FIELD = "sizeBytes"; - private static final String ALIVE_DOCUMENTS_COUNT = "aliveDocumentsCount"; - private static final String ALIVE_NAMESPACES_COUNT = "aliveNamespacesCount"; - - private final Bundle mBundle; - - StorageInfo(@NonNull Bundle bundle) { - mBundle = Objects.requireNonNull(bundle); - } - - /** - * Returns the {@link Bundle} populated by this builder. - * - * @hide - */ - @NonNull - public Bundle getBundle() { - return mBundle; - } - - /** Returns the estimated size of the session's database in bytes. */ - public long getSizeBytes() { - return mBundle.getLong(SIZE_BYTES_FIELD); - } - - /** - * Returns the number of alive documents in the current session. - * - *

Alive documents are documents that haven't been deleted and haven't exceeded the ttl as - * set in {@link GenericDocument.Builder#setTtlMillis}. - */ - public int getAliveDocumentsCount() { - return mBundle.getInt(ALIVE_DOCUMENTS_COUNT); - } - - /** - * Returns the number of namespaces that have at least one alive document in the current - * session's database. - * - *

Alive documents are documents that haven't been deleted and haven't exceeded the ttl as - * set in {@link GenericDocument.Builder#setTtlMillis}. - */ - public int getAliveNamespacesCount() { - return mBundle.getInt(ALIVE_NAMESPACES_COUNT); - } - - /** Builder for {@link StorageInfo} objects. */ - public static final class Builder { - private long mSizeBytes; - private int mAliveDocumentsCount; - private int mAliveNamespacesCount; - - /** Sets the size in bytes. */ - @NonNull - public StorageInfo.Builder setSizeBytes(long sizeBytes) { - mSizeBytes = sizeBytes; - return this; - } - - /** Sets the number of alive documents. */ - @NonNull - public StorageInfo.Builder setAliveDocumentsCount(int aliveDocumentsCount) { - mAliveDocumentsCount = aliveDocumentsCount; - return this; - } - - /** Sets the number of alive namespaces. */ - @NonNull - public StorageInfo.Builder setAliveNamespacesCount(int aliveNamespacesCount) { - mAliveNamespacesCount = aliveNamespacesCount; - return this; - } - - /** Builds a {@link StorageInfo} object. */ - @NonNull - public StorageInfo build() { - Bundle bundle = new Bundle(); - bundle.putLong(SIZE_BYTES_FIELD, mSizeBytes); - bundle.putInt(ALIVE_DOCUMENTS_COUNT, mAliveDocumentsCount); - bundle.putInt(ALIVE_NAMESPACES_COUNT, mAliveNamespacesCount); - return new StorageInfo(bundle); - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java deleted file mode 100644 index 62593ae8fc298..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/AppSearchException.java +++ /dev/null @@ -1,85 +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.exceptions; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.AppSearchResult; - -/** - * An exception thrown by {@link android.app.appsearch.AppSearchSession} or a subcomponent. - * - *

These exceptions can be converted into a failed {@link AppSearchResult} for propagating to the - * client. - */ -public class AppSearchException extends Exception { - private final @AppSearchResult.ResultCode int mResultCode; - - /** - * Initializes an {@link AppSearchException} with no message. - * - * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}. - */ - public AppSearchException(@AppSearchResult.ResultCode int resultCode) { - this(resultCode, /*message=*/ null); - } - - /** - * Initializes an {@link AppSearchException} with a result code and message. - * - * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}. - * @param message The detail message (which is saved for later retrieval by the {@link - * #getMessage()} method). - */ - public AppSearchException( - @AppSearchResult.ResultCode int resultCode, @Nullable String message) { - this(resultCode, message, /*cause=*/ null); - } - - /** - * Initializes an {@link AppSearchException} with a result code, message and cause. - * - * @param resultCode One of the constants documented in {@link AppSearchResult#getResultCode}. - * @param message The detail message (which is saved for later retrieval by the {@link - * #getMessage()} method). - * @param cause The cause (which is saved for later retrieval by the {@link #getCause()} - * method). (A null value is permitted, and indicates that the cause is nonexistent or - * unknown.) - */ - public AppSearchException( - @AppSearchResult.ResultCode int resultCode, - @Nullable String message, - @Nullable Throwable cause) { - super(message, cause); - mResultCode = resultCode; - } - - /** - * Returns the result code this exception was constructed with. - * - * @return One of the constants documented in {@link AppSearchResult#getResultCode}. - */ - public @AppSearchResult.ResultCode int getResultCode() { - return mResultCode; - } - - /** Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult}. */ - @NonNull - public AppSearchResult toAppSearchResult() { - return AppSearchResult.newFailedResult(mResultCode, getMessage()); - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSchemaException.java b/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSchemaException.java deleted file mode 100644 index 5f8da7fe067a9..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/exceptions/IllegalSchemaException.java +++ /dev/null @@ -1,36 +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.exceptions; - -import android.annotation.NonNull; - -/** - * Indicates that a {@link android.app.appsearch.AppSearchSchema} has logical inconsistencies such - * as unpopulated mandatory fields or illegal combinations of parameters. - * - * @hide - */ -public class IllegalSchemaException extends IllegalArgumentException { - /** - * Constructs a new {@link IllegalSchemaException}. - * - * @param message A developer-readable description of the issue with the bundle. - */ - public IllegalSchemaException(@NonNull String message) { - super(message); - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java deleted file mode 100644 index e77043f3867cd..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/util/BundleUtil.java +++ /dev/null @@ -1,251 +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.util; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Bundle; -import android.os.Parcel; -import android.util.SparseArray; - -import java.util.ArrayList; -import java.util.Arrays; - -/** - * Utilities for working with {@link android.os.Bundle}. - * - * @hide - */ -public final class BundleUtil { - private BundleUtil() {} - - /** - * Deeply checks two bundles are equal or not. - * - *

Two bundles will be considered equal if they contain the same keys, and each value is also - * equal. Bundle values are compared using deepEquals. - */ - public static boolean deepEquals(@Nullable Bundle one, @Nullable Bundle two) { - if (one == null && two == null) { - return true; - } - if (one == null || two == null) { - return false; - } - if (one.size() != two.size()) { - return false; - } - if (!one.keySet().equals(two.keySet())) { - return false; - } - // Bundle inherit its equals() from Object.java, which only compare their memory address. - // We should iterate all keys and check their presents and values in both bundle. - for (String key : one.keySet()) { - if (!bundleValueEquals(one.get(key), two.get(key))) { - return false; - } - } - return true; - } - - /** - * Deeply checks whether two values in a Bundle are equal or not. - * - *

Values of type Bundle are compared using {@link #deepEquals}. - */ - private static boolean bundleValueEquals(@Nullable Object one, @Nullable Object two) { - if (one == null && two == null) { - return true; - } - if (one == null || two == null) { - return false; - } - if (one.equals(two)) { - return true; - } - if (one instanceof Bundle && two instanceof Bundle) { - return deepEquals((Bundle) one, (Bundle) two); - } else if (one instanceof int[] && two instanceof int[]) { - return Arrays.equals((int[]) one, (int[]) two); - } else if (one instanceof byte[] && two instanceof byte[]) { - return Arrays.equals((byte[]) one, (byte[]) two); - } else if (one instanceof char[] && two instanceof char[]) { - return Arrays.equals((char[]) one, (char[]) two); - } else if (one instanceof long[] && two instanceof long[]) { - return Arrays.equals((long[]) one, (long[]) two); - } else if (one instanceof float[] && two instanceof float[]) { - return Arrays.equals((float[]) one, (float[]) two); - } else if (one instanceof short[] && two instanceof short[]) { - return Arrays.equals((short[]) one, (short[]) two); - } else if (one instanceof double[] && two instanceof double[]) { - return Arrays.equals((double[]) one, (double[]) two); - } else if (one instanceof boolean[] && two instanceof boolean[]) { - return Arrays.equals((boolean[]) one, (boolean[]) two); - } else if (one instanceof Object[] && two instanceof Object[]) { - Object[] arrayOne = (Object[]) one; - Object[] arrayTwo = (Object[]) two; - if (arrayOne.length != arrayTwo.length) { - return false; - } - if (Arrays.equals(arrayOne, arrayTwo)) { - return true; - } - for (int i = 0; i < arrayOne.length; i++) { - if (!bundleValueEquals(arrayOne[i], arrayTwo[i])) { - return false; - } - } - return true; - } else if (one instanceof ArrayList && two instanceof ArrayList) { - ArrayList listOne = (ArrayList) one; - ArrayList listTwo = (ArrayList) two; - if (listOne.size() != listTwo.size()) { - return false; - } - for (int i = 0; i < listOne.size(); i++) { - if (!bundleValueEquals(listOne.get(i), listTwo.get(i))) { - return false; - } - } - return true; - } else if (one instanceof SparseArray && two instanceof SparseArray) { - SparseArray arrayOne = (SparseArray) one; - SparseArray arrayTwo = (SparseArray) two; - if (arrayOne.size() != arrayTwo.size()) { - return false; - } - for (int i = 0; i < arrayOne.size(); i++) { - if (arrayOne.keyAt(i) != arrayTwo.keyAt(i) - || !bundleValueEquals(arrayOne.valueAt(i), arrayTwo.valueAt(i))) { - return false; - } - } - return true; - } - return false; - } - - /** - * Calculates the hash code for a bundle. - * - *

The hash code is only effected by the contents in the bundle. Bundles will get consistent - * hash code if they have same contents. - */ - public static int deepHashCode(@Nullable Bundle bundle) { - if (bundle == null) { - return 0; - } - int[] hashCodes = new int[bundle.size() + 1]; - int hashCodeIdx = 0; - // Bundle inherit its hashCode() from Object.java, which only relative to their memory - // address. Bundle doesn't have an order, so we should iterate all keys and combine - // their value's hashcode into an array. And use the hashcode of the array to be - // the hashcode of the bundle. - // Because bundle.keySet() doesn't guarantee any particular order, we need to sort the keys - // in case the iteration order varies from run to run. - String[] keys = bundle.keySet().toArray(new String[0]); - Arrays.sort(keys); - // Hash the keys so we can detect key-only differences - hashCodes[hashCodeIdx++] = Arrays.hashCode(keys); - for (int keyIdx = 0; keyIdx < keys.length; keyIdx++) { - Object value = bundle.get(keys[keyIdx]); - if (value instanceof Bundle) { - hashCodes[hashCodeIdx++] = deepHashCode((Bundle) value); - } else if (value instanceof int[]) { - hashCodes[hashCodeIdx++] = Arrays.hashCode((int[]) value); - } else if (value instanceof byte[]) { - hashCodes[hashCodeIdx++] = Arrays.hashCode((byte[]) value); - } else if (value instanceof char[]) { - hashCodes[hashCodeIdx++] = Arrays.hashCode((char[]) value); - } else if (value instanceof long[]) { - hashCodes[hashCodeIdx++] = Arrays.hashCode((long[]) value); - } else if (value instanceof float[]) { - hashCodes[hashCodeIdx++] = Arrays.hashCode((float[]) value); - } else if (value instanceof short[]) { - hashCodes[hashCodeIdx++] = Arrays.hashCode((short[]) value); - } else if (value instanceof double[]) { - hashCodes[hashCodeIdx++] = Arrays.hashCode((double[]) value); - } else if (value instanceof boolean[]) { - hashCodes[hashCodeIdx++] = Arrays.hashCode((boolean[]) value); - } else if (value instanceof String[]) { - // Optimization to avoid Object[] handler creating an inner array for common cases - hashCodes[hashCodeIdx++] = Arrays.hashCode((String[]) value); - } else if (value instanceof Object[]) { - Object[] array = (Object[]) value; - int[] innerHashCodes = new int[array.length]; - for (int j = 0; j < array.length; j++) { - if (array[j] instanceof Bundle) { - innerHashCodes[j] = deepHashCode((Bundle) array[j]); - } else if (array[j] != null) { - innerHashCodes[j] = array[j].hashCode(); - } - } - hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes); - } else if (value instanceof ArrayList) { - ArrayList list = (ArrayList) value; - int[] innerHashCodes = new int[list.size()]; - for (int j = 0; j < innerHashCodes.length; j++) { - Object item = list.get(j); - if (item instanceof Bundle) { - innerHashCodes[j] = deepHashCode((Bundle) item); - } else if (item != null) { - innerHashCodes[j] = item.hashCode(); - } - } - hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes); - } else if (value instanceof SparseArray) { - SparseArray array = (SparseArray) value; - int[] innerHashCodes = new int[array.size() * 2]; - for (int j = 0; j < array.size(); j++) { - innerHashCodes[j * 2] = array.keyAt(j); - Object item = array.valueAt(j); - if (item instanceof Bundle) { - innerHashCodes[j * 2 + 1] = deepHashCode((Bundle) item); - } else if (item != null) { - innerHashCodes[j * 2 + 1] = item.hashCode(); - } - } - hashCodes[hashCodeIdx++] = Arrays.hashCode(innerHashCodes); - } else { - hashCodes[hashCodeIdx++] = value.hashCode(); - } - } - return Arrays.hashCode(hashCodes); - } - - /** - * Deeply clones a Bundle. - * - *

Values which are Bundles, Lists or Arrays are deeply copied themselves. - */ - @NonNull - public static Bundle deepCopy(@NonNull Bundle bundle) { - // Write bundle to bytes - Parcel parcel = Parcel.obtain(); - try { - parcel.writeBundle(bundle); - byte[] serializedMessage = parcel.marshall(); - - // Read bundle from bytes - parcel.unmarshall(serializedMessage, 0, serializedMessage.length); - parcel.setDataPosition(0); - return parcel.readBundle(); - } finally { - parcel.recycle(); - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/IndentingStringBuilder.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/IndentingStringBuilder.java deleted file mode 100644 index b494c3ce9337d..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/util/IndentingStringBuilder.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch.util; - -import android.annotation.NonNull; - -/** - * Utility for building indented strings. - * - *

This is a wrapper for {@link StringBuilder} for appending strings with indentation. The - * indentation level can be increased by calling {@link #increaseIndentLevel()} and decreased by - * calling {@link #decreaseIndentLevel()}. - * - *

Indentation is applied after each newline character for the given indent level. - * - * @hide - */ -public class IndentingStringBuilder { - private final StringBuilder mStringBuilder = new StringBuilder(); - - // Indicates whether next non-newline character should have an indent applied before it. - private boolean mIndentNext = false; - private int mIndentLevel = 0; - - /** Increases the indent level by one for appended strings. */ - @NonNull - public IndentingStringBuilder increaseIndentLevel() { - mIndentLevel++; - return this; - } - - /** Decreases the indent level by one for appended strings. */ - @NonNull - public IndentingStringBuilder decreaseIndentLevel() throws IllegalStateException { - if (mIndentLevel == 0) { - throw new IllegalStateException("Cannot set indent level below 0."); - } - mIndentLevel--; - return this; - } - - /** - * Appends provided {@code String} at the current indentation level. - * - *

Indentation is applied after each newline character. - */ - @NonNull - public IndentingStringBuilder append(@NonNull String str) { - applyIndentToString(str); - return this; - } - - /** - * Appends provided {@code Object}, represented as a {@code String}, at the current indentation - * level. - * - *

Indentation is applied after each newline character. - */ - @NonNull - public IndentingStringBuilder append(@NonNull Object obj) { - applyIndentToString(obj.toString()); - return this; - } - - @Override - @NonNull - public String toString() { - return mStringBuilder.toString(); - } - - /** Adds indent string to the {@link StringBuilder} instance for current indent level. */ - private void applyIndent() { - for (int i = 0; i < mIndentLevel; i++) { - mStringBuilder.append(" "); - } - } - - /** - * Applies indent, for current indent level, after each newline character. - * - *

Consecutive newline characters are not indented. - */ - private void applyIndentToString(@NonNull String str) { - int index = str.indexOf("\n"); - if (index == 0) { - // String begins with new line character: append newline and slide past newline. - mStringBuilder.append("\n"); - mIndentNext = true; - if (str.length() > 1) { - applyIndentToString(str.substring(index + 1)); - } - } else if (index >= 1) { - // String contains new line character: divide string between newline, append new line, - // and recurse on each string. - String beforeIndentString = str.substring(0, index); - applyIndentToString(beforeIndentString); - mStringBuilder.append("\n"); - mIndentNext = true; - if (str.length() > index + 1) { - String afterIndentString = str.substring(index + 1); - applyIndentToString(afterIndentString); - } - } else { - // String does not contain newline character: append string. - if (mIndentNext) { - applyIndent(); - mIndentNext = false; - } - mStringBuilder.append(str); - } - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/LogUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/LogUtil.java deleted file mode 100644 index f2cc3b969a60d..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/util/LogUtil.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch.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. - * - *

- * - *

    - *
  • 0: no tracing. - *
  • 1: fast tracing (statuses/counts only) - *
  • 2: full tracing (complete messages) - *
- */ - 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. - * - *

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. - * - *

If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately. - * - *

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. - * - *

If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately. - * - *

If {@link #PII_TRACE_LEVEL} is 1, {@code fastTraceObj} is logged if it is non-null. - * - *

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()); - } -} diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java b/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java deleted file mode 100644 index 10e014bf9c9a5..0000000000000 --- a/apex/appsearch/framework/java/external/android/app/appsearch/util/SchemaMigrationUtil.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch.util; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchResult; -import android.app.appsearch.AppSearchSchema; -import android.app.appsearch.Migrator; -import android.app.appsearch.SetSchemaResponse; -import android.app.appsearch.exceptions.AppSearchException; -import android.util.ArrayMap; -import android.util.ArraySet; - -import java.util.Collections; -import java.util.Map; -import java.util.Set; - -/** - * Utilities for schema migration. - * - * @hide - */ -public final class SchemaMigrationUtil { - private SchemaMigrationUtil() {} - - /** - * Returns all active {@link Migrator}s that need to be triggered in this migration. - * - *

{@link Migrator#shouldMigrate} returns {@code true} will make the {@link Migrator} active. - */ - @NonNull - public static Map getActiveMigrators( - @NonNull Set existingSchemas, - @NonNull Map migrators, - int currentVersion, - int finalVersion) { - if (currentVersion == finalVersion) { - return Collections.emptyMap(); - } - Set existingTypes = new ArraySet<>(existingSchemas.size()); - for (AppSearchSchema schema : existingSchemas) { - existingTypes.add(schema.getSchemaType()); - } - - Map activeMigrators = new ArrayMap<>(); - for (Map.Entry entry : migrators.entrySet()) { - // The device contains the source type, and we should trigger migration for the type. - String schemaType = entry.getKey(); - Migrator migrator = entry.getValue(); - if (existingTypes.contains(schemaType) - && migrator.shouldMigrate(currentVersion, finalVersion)) { - activeMigrators.put(schemaType, migrator); - } - } - return activeMigrators; - } - - /** - * Checks the setSchema() call won't delete any types or has incompatible types after all {@link - * Migrator} has been triggered.. - */ - public static void checkDeletedAndIncompatibleAfterMigration( - @NonNull SetSchemaResponse setSchemaResponse, @NonNull Set activeMigrators) - throws AppSearchException { - Set unmigratedIncompatibleTypes = - new ArraySet<>(setSchemaResponse.getIncompatibleTypes()); - unmigratedIncompatibleTypes.removeAll(activeMigrators); - - Set unmigratedDeletedTypes = new ArraySet<>(setSchemaResponse.getDeletedTypes()); - unmigratedDeletedTypes.removeAll(activeMigrators); - - // check if there are any unmigrated incompatible types or deleted types. If there - // are, we will getActiveMigratorsthrow an exception. That's the only case we - // swallowed in the AppSearchImpl#setSchema(). - // Since the force override is false, the schema will not have been set if there are - // any incompatible or deleted types. - checkDeletedAndIncompatible(unmigratedDeletedTypes, unmigratedIncompatibleTypes); - } - - /** Checks the setSchema() call won't delete any types or has incompatible types. */ - public static void checkDeletedAndIncompatible( - @NonNull Set deletedTypes, @NonNull Set incompatibleTypes) - throws AppSearchException { - if (deletedTypes.size() > 0 || incompatibleTypes.size() > 0) { - String newMessage = - "Schema is incompatible." - + "\n Deleted types: " - + deletedTypes - + "\n Incompatible types: " - + incompatibleTypes; - throw new AppSearchException(AppSearchResult.RESULT_INVALID_SCHEMA, newMessage); - } - } -} diff --git a/apex/appsearch/service/Android.bp b/apex/appsearch/service/Android.bp deleted file mode 100644 index b6521ffff51b3..0000000000000 --- a/apex/appsearch/service/Android.bp +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (C) 2019 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -genrule { - name: "statslog-appsearch-java-gen", - tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --java $(out) --module appsearch --javaPackage com.android.server.appsearch.stats --javaClass AppSearchStatsLog", - out: ["com/android/server/appsearch/stats/AppSearchStatsLog.java"], -} - -java_library { - name: "statslog-appsearch-lib", - srcs: [":statslog-appsearch-java-gen"], - libs: [ - "framework-statsd.stubs.module_lib", - ], - sdk_version: "system_server_current", - apex_available: ["com.android.appsearch"], -} - -java_library { - name: "service-appsearch", - srcs: ["java/**/*.java"], - sdk_version: "system_server_current", - static_libs: [ - "icing-java-proto-lite", - "libicing-java", - "statslog-appsearch-lib", - // Entries below this line are outside of the appsearch package tree and must be kept in - // sync with jarjar.txt - "modules-utils-preconditions", - ], - libs: [ - "framework-appsearch.impl", - "framework-statsd.stubs.module_lib", - ], - defaults: ["framework-system-server-module-defaults"], - permitted_packages: [ - "com.android.server.appsearch", - "com.google.android.icing", - ], - jarjar_rules: "jarjar-rules.txt", - visibility: [ - // These are required until appsearch is properly unbundled. - "//frameworks/base/services/tests/mockingservicestests", - "//frameworks/base/services/tests/servicestests", - ], - apex_available: ["com.android.appsearch"], - installable: true, -} diff --git a/apex/appsearch/service/jarjar-rules.txt b/apex/appsearch/service/jarjar-rules.txt deleted file mode 100644 index c79ea22ca541c..0000000000000 --- a/apex/appsearch/service/jarjar-rules.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Rename all icing classes to match our module name. OEMs could start using icing lib for some other -# purpose in system service, which would cause class collisions when loading our apex into the -# system service. -rule com.google.protobuf.** com.android.server.appsearch.protobuf.@1 -rule com.google.android.icing.proto.** com.android.server.appsearch.icing.proto.@1 - -# Rename all com.android.internal.util classes to prevent class name collisions -# between this module and the other versions of the utility classes linked into -# the framework. - -# These must be kept in sync with the sources of framework-utils-appsearch -rule com.android.internal.util.Preconditions* com.android.server.appsearch.internal.util.Preconditions@1 diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java deleted file mode 100644 index 29048b25ad6f0..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchConfig.java +++ /dev/null @@ -1,454 +0,0 @@ -/* - * 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 android.os.Bundle; -import android.provider.DeviceConfig; -import android.provider.DeviceConfig.OnPropertiesChangedListener; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; - -import java.util.Objects; -import java.util.concurrent.Executor; - -/** - * It contains all the keys for the flags, as well as caches some of latest flag values from - * DeviceConfig. - * - *

Though the latest flag values can always be retrieved by calling {@code - * DeviceConfig.getProperty}, we want to cache some of those values. For example, the sampling - * intervals for logging, they are needed for each api call and it would be a little expensive to - * call - * {@code DeviceConfig.getProperty} every time. - * - *

Listener is registered to DeviceConfig keep the cached value up to date. - * - *

This class is thread-safe. - * - * @hide - */ -public final class AppSearchConfig implements AutoCloseable { - private static volatile AppSearchConfig sConfig; - - /** - * It would be used as default min time interval between samples in millis if there is no value - * set for {@link AppSearchConfig#KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS} in DeviceConfig. - */ - @VisibleForTesting - static final long DEFAULT_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 50; - - /** - * It would be used as default sampling interval if there is no value - * set for {@link AppSearchConfig#KEY_SAMPLING_INTERVAL_DEFAULT} in DeviceConfig. - */ - @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; - @VisibleForTesting - static final int DEFAULT_BYTES_OPTIMIZE_THRESHOLD = 1 * 1024 * 1024; // 1 MiB - @VisibleForTesting - static final int DEFAULT_TIME_OPTIMIZE_THRESHOLD_MILLIS = Integer.MAX_VALUE; - @VisibleForTesting - static final int DEFAULT_DOC_COUNT_OPTIMIZE_THRESHOLD = 10_000; - - /* - * Keys for ALL the flags stored in DeviceConfig. - */ - public static final String KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = - "min_time_interval_between_samples_millis"; - public static final String KEY_SAMPLING_INTERVAL_DEFAULT = "sampling_interval_default"; - public static final String KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS = - "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_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS = - "sampling_interval_for_initialize_stats"; - public static final String KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS = - "sampling_interval_for_search_stats"; - public static final String KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS = - "sampling_interval_for_global_search_stats"; - public static final String KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS = - "sampling_interval_for_optimize_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"; - public static final String KEY_BYTES_OPTIMIZE_THRESHOLD = "bytes_optimize_threshold"; - public static final String KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS = "time_optimize_threshold"; - public static final String KEY_DOC_COUNT_OPTIMIZE_THRESHOLD = "doc_count_optimize_threshold"; - - // 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_INITIALIZE_STATS, - KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS, - KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS, - KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS, - KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES, - KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT, - KEY_BYTES_OPTIMIZE_THRESHOLD, - KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS, - KEY_DOC_COUNT_OPTIMIZE_THRESHOLD - }; - - // Lock needed for all the operations in this class. - private final Object mLock = new Object(); - - /** - * Bundle to hold all the cached flag values corresponding to - * {@link AppSearchConfig#KEYS_TO_ALL_CACHED_VALUES}. - */ - @GuardedBy("mLock") - private final Bundle mBundleLocked = new Bundle(); - - - @GuardedBy("mLock") - private boolean mIsClosedLocked = false; - - /** Listener to update cached flag values from DeviceConfig. */ - private final OnPropertiesChangedListener mOnDeviceConfigChangedListener = - properties -> { - if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_APPSEARCH)) { - return; - } - - updateCachedValues(properties); - }; - - private AppSearchConfig() { - } - - /** - * Creates an instance of {@link AppSearchConfig}. - * - * @param executor used to fetch and cache the flag values from DeviceConfig during creation or - * config change. - */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - @NonNull - public static AppSearchConfig create(@NonNull Executor executor) { - Objects.requireNonNull(executor); - AppSearchConfig configManager = new AppSearchConfig(); - configManager.initialize(executor); - return configManager; - } - - /** - * Gets an instance of {@link AppSearchConfig} to be used. - * - *

If no instance has been initialized yet, a new one will be created. Otherwise, the - * existing instance will be returned. - */ - @NonNull - public static AppSearchConfig getInstance(@NonNull Executor executor) { - Objects.requireNonNull(executor); - if (sConfig == null) { - synchronized (AppSearchConfig.class) { - if (sConfig == null) { - sConfig = create(executor); - } - } - } - return sConfig; - } - - /** - * Initializes the {@link AppSearchConfig} - * - *

It fetches the custom properties from DeviceConfig if available. - * - * @param executor listener would be run on to handle P/H flag change. - */ - private void initialize(@NonNull Executor executor) { - executor.execute(() -> { - // Attach the callback to get updates on those properties. - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APPSEARCH, - executor, - mOnDeviceConfigChangedListener); - - DeviceConfig.Properties properties = DeviceConfig.getProperties( - DeviceConfig.NAMESPACE_APPSEARCH, KEYS_TO_ALL_CACHED_VALUES); - updateCachedValues(properties); - }); - } - - // TODO(b/173532925) check this will be called. If we have a singleton instance for this - // class, probably we don't need it. - @Override - public void close() { - synchronized (mLock) { - if (mIsClosedLocked) { - return; - } - - DeviceConfig.removeOnPropertiesChangedListener(mOnDeviceConfigChangedListener); - mIsClosedLocked = true; - } - } - - /** Returns cached value for minTimeIntervalBetweenSamplesMillis. */ - public long getCachedMinTimeIntervalBetweenSamplesMillis() { - synchronized (mLock) { - throwIfClosedLocked(); - return mBundleLocked.getLong(KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - DEFAULT_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS); - } - } - - /** - * Returns cached value for default sampling interval for all the stats NOT listed in - * the configuration. - * - *

For example, sampling_interval=10 means that one out of every 10 stats was logged. - */ - public int getCachedSamplingIntervalDefault() { - synchronized (mLock) { - throwIfClosedLocked(); - return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_DEFAULT, DEFAULT_SAMPLING_INTERVAL); - } - } - - /** - * Returns cached value for sampling interval for batch calls. - * - *

For example, sampling_interval=10 means that one out of every 10 stats was logged. - */ - public int getCachedSamplingIntervalForBatchCallStats() { - synchronized (mLock) { - throwIfClosedLocked(); - return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, - getCachedSamplingIntervalDefault()); - } - } - - /** - * Returns cached value for sampling interval for putDocument. - * - *

For example, sampling_interval=10 means that one out of every 10 stats was logged. - */ - public int getCachedSamplingIntervalForPutDocumentStats() { - synchronized (mLock) { - throwIfClosedLocked(); - return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, - getCachedSamplingIntervalDefault()); - } - } - - /** - * Returns cached value for sampling interval for initialize. - * - *

For example, sampling_interval=10 means that one out of every 10 stats was logged. - */ - public int getCachedSamplingIntervalForInitializeStats() { - synchronized (mLock) { - throwIfClosedLocked(); - return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS, - getCachedSamplingIntervalDefault()); - } - } - - /** - * Returns cached value for sampling interval for search. - * - *

For example, sampling_interval=10 means that one out of every 10 stats was logged. - */ - public int getCachedSamplingIntervalForSearchStats() { - synchronized (mLock) { - throwIfClosedLocked(); - return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS, - getCachedSamplingIntervalDefault()); - } - } - - /** - * Returns cached value for sampling interval for globalSearch. - * - *

For example, sampling_interval=10 means that one out of every 10 stats was logged. - */ - public int getCachedSamplingIntervalForGlobalSearchStats() { - synchronized (mLock) { - throwIfClosedLocked(); - return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS, - getCachedSamplingIntervalDefault()); - } - } - - /** - * Returns cached value for sampling interval for optimize. - * - *

For example, sampling_interval=10 means that one out of every 10 stats was logged. - */ - public int getCachedSamplingIntervalForOptimizeStats() { - synchronized (mLock) { - throwIfClosedLocked(); - return mBundleLocked.getInt(KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS, - getCachedSamplingIntervalDefault()); - } - } - - /** 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); - } - } - - /** - * Returns the cached optimize byte size threshold. - * - * An AppSearch Optimize job will be triggered if the bytes size of garbage resource exceeds - * this threshold. - */ - int getCachedBytesOptimizeThreshold() { - synchronized (mLock) { - throwIfClosedLocked(); - return mBundleLocked.getInt(KEY_BYTES_OPTIMIZE_THRESHOLD, - DEFAULT_BYTES_OPTIMIZE_THRESHOLD); - } - } - - /** - * Returns the cached optimize time interval threshold. - * - * An AppSearch Optimize job will be triggered if the time since last optimize job exceeds - * this threshold. - */ - int getCachedTimeOptimizeThresholdMs() { - synchronized (mLock) { - throwIfClosedLocked(); - return mBundleLocked.getInt(KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS, - DEFAULT_TIME_OPTIMIZE_THRESHOLD_MILLIS); - } - } - - /** - * Returns the cached optimize document count threshold threshold. - * - * An AppSearch Optimize job will be triggered if the number of document of garbage resource - * exceeds this threshold. - */ - int getCachedDocCountOptimizeThreshold() { - synchronized (mLock) { - throwIfClosedLocked(); - return mBundleLocked.getInt(KEY_DOC_COUNT_OPTIMIZE_THRESHOLD, - DEFAULT_DOC_COUNT_OPTIMIZE_THRESHOLD); - } - } - - @GuardedBy("mLock") - private void throwIfClosedLocked() { - if (mIsClosedLocked) { - throw new IllegalStateException("Trying to use a closed AppSearchConfig instance."); - } - } - - private void updateCachedValues(@NonNull DeviceConfig.Properties properties) { - for (String key : properties.getKeyset()) { - updateCachedValue(key, properties); - } - } - - private void updateCachedValue(@NonNull String key, - @NonNull DeviceConfig.Properties properties) { - if (properties.getString(key, /*defaultValue=*/ null) == null) { - // Key is missing or value is just null. That is not expected if the key is - // defined in the configuration. - // - // We choose NOT to put the default value in the bundle. - // Instead, we let the getters handle what default value should be returned. - // - // Also we keep the old value in the bundle. So getters can still - // return last valid value. - return; - } - - switch (key) { - case KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS: - synchronized (mLock) { - mBundleLocked.putLong(key, - properties.getLong(key, - DEFAULT_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS)); - } - break; - case KEY_SAMPLING_INTERVAL_DEFAULT: - case KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS: - case KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS: - case KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS: - case KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS: - case KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS: - case KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS: - synchronized (mLock) { - 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; - case KEY_BYTES_OPTIMIZE_THRESHOLD: - synchronized (mLock) { - mBundleLocked.putInt(key, properties.getInt(key, - DEFAULT_BYTES_OPTIMIZE_THRESHOLD)); - } - break; - case KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS: - synchronized (mLock) { - mBundleLocked.putInt(key, properties.getInt(key, - DEFAULT_TIME_OPTIMIZE_THRESHOLD_MILLIS)); - } - break; - case KEY_DOC_COUNT_OPTIMIZE_THRESHOLD: - synchronized (mLock) { - mBundleLocked.putInt(key, properties.getInt(key, - DEFAULT_DOC_COUNT_OPTIMIZE_THRESHOLD)); - } - break; - default: - break; - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java deleted file mode 100644 index db23a6dc3047f..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ /dev/null @@ -1,1522 +0,0 @@ -/* - * Copyright (C) 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 com.android.server.appsearch; - -import static android.app.appsearch.AppSearchResult.throwableToFailedResult; -import static android.os.Process.INVALID_UID; - -import android.annotation.ElapsedRealtimeLong; -import android.annotation.NonNull; -import android.app.appsearch.AppSearchBatchResult; -import android.app.appsearch.AppSearchMigrationHelper; -import android.app.appsearch.AppSearchResult; -import android.app.appsearch.AppSearchSchema; -import android.app.appsearch.GenericDocument; -import android.app.appsearch.GetSchemaResponse; -import android.app.appsearch.PackageIdentifier; -import android.app.appsearch.SearchResultPage; -import android.app.appsearch.SearchSpec; -import android.app.appsearch.SetSchemaResponse; -import android.app.appsearch.StorageInfo; -import android.app.appsearch.aidl.AppSearchBatchResultParcel; -import android.app.appsearch.aidl.AppSearchResultParcel; -import android.app.appsearch.aidl.IAppSearchBatchResultCallback; -import android.app.appsearch.aidl.IAppSearchManager; -import android.app.appsearch.aidl.IAppSearchResultCallback; -import android.app.appsearch.exceptions.AppSearchException; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageStats; -import android.os.Binder; -import android.os.Bundle; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.server.LocalManagerRegistry; -import com.android.server.SystemService; -import com.android.server.appsearch.external.localstorage.stats.CallStats; -import com.android.server.appsearch.external.localstorage.stats.OptimizeStats; -import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore; -import com.android.server.appsearch.stats.StatsCollector; -import com.android.server.appsearch.util.PackageUtil; -import com.android.server.usage.StorageStatsManagerLocal; -import com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter; - -import com.google.android.icing.proto.PersistType; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -/** - * The main service implementation which contains AppSearch's platform functionality. - * @hide - */ -public class AppSearchManagerService extends SystemService { - private static final String TAG = "AppSearchManagerService"; - private final Context mContext; - private PackageManager mPackageManager; - private UserManager mUserManager; - private AppSearchUserInstanceManager mAppSearchUserInstanceManager; - - // Never call shutdownNow(). It will cancel the futures it's returned. And since - // Executor#execute won't return anything, we will hang forever waiting for the execution. - // AppSearch multi-thread execution is guarded by Read & Write Lock in AppSearchImpl, all - // mutate requests will need to gain write lock and query requests need to gain read lock. - private static final Executor EXECUTOR = new ThreadPoolExecutor(/*corePoolSize=*/1, - Runtime.getRuntime().availableProcessors(), /*keepAliveTime*/ 60L, TimeUnit.SECONDS, - new LinkedBlockingQueue<>()); - - // Cache of unlocked users so we don't have to query UserManager service each time. The "locked" - // suffix refers to the fact that access to the field should be locked; unrelated to the - // unlocked status of users. - @GuardedBy("mUnlockedUsersLocked") - private final Set mUnlockedUsersLocked = new ArraySet<>(); - - public AppSearchManagerService(Context context) { - super(context); - mContext = context; - } - - @Override - public void onStart() { - publishBinderService(Context.APP_SEARCH_SERVICE, new Stub()); - mPackageManager = getContext().getPackageManager(); - mAppSearchUserInstanceManager = AppSearchUserInstanceManager.getInstance(); - mUserManager = mContext.getSystemService(UserManager.class); - registerReceivers(); - LocalManagerRegistry.getManager(StorageStatsManagerLocal.class) - .registerStorageStatsAugmenter(new AppSearchStorageStatsAugmenter(), TAG); - } - - @Override - public void onBootPhase(/* @BootPhase */ int phase) { - if (phase == PHASE_BOOT_COMPLETED) { - StatsCollector.getInstance(mContext, EXECUTOR); - } - } - - private void registerReceivers() { - mContext.registerReceiverForAllUsers( - new UserActionReceiver(), - new IntentFilter(Intent.ACTION_USER_REMOVED), - /*broadcastPermission=*/ null, - /*scheduler=*/ null); - - //TODO(b/145759910) Add a direct callback when user clears the data instead of relying on - // broadcasts - IntentFilter packageChangedFilter = new IntentFilter(); - packageChangedFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); - packageChangedFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); - packageChangedFilter.addDataScheme("package"); - packageChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); - mContext.registerReceiverForAllUsers( - new PackageChangedReceiver(), - packageChangedFilter, - /*broadcastPermission=*/ null, - /*scheduler=*/ null); - } - - private class UserActionReceiver extends BroadcastReceiver { - @Override - public void onReceive(@NonNull Context context, @NonNull Intent intent) { - Objects.requireNonNull(context); - Objects.requireNonNull(intent); - - switch (intent.getAction()) { - case Intent.ACTION_USER_REMOVED: - UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER); - if (userHandle == null) { - Log.e(TAG, "Extra " - + Intent.EXTRA_USER + " is missing in the intent: " + intent); - return; - } - handleUserRemoved(userHandle); - break; - default: - Log.e(TAG, "Received unknown intent: " + intent); - } - } - } - - /** - * Handles user removed action. - * - *

Only need to clear the AppSearchImpl instance. The data of AppSearch is saved in the - * "credential encrypted" system directory of each user. That directory will be auto-deleted - * when a user is removed. - * - * @param userHandle The multi-user handle of the user that need to be removed. - * - * @see android.os.Environment#getDataSystemCeDirectory - */ - private void handleUserRemoved(@NonNull UserHandle userHandle) { - try { - mAppSearchUserInstanceManager.closeAndRemoveUserInstance(userHandle); - Log.i(TAG, "Removed AppSearchImpl instance for: " + userHandle); - } catch (Throwable t) { - Log.e(TAG, "Unable to remove data for: " + userHandle, t); - } - } - - private class PackageChangedReceiver extends BroadcastReceiver { - @Override - public void onReceive(@NonNull Context context, @NonNull Intent intent) { - Objects.requireNonNull(context); - Objects.requireNonNull(intent); - - switch (intent.getAction()) { - case Intent.ACTION_PACKAGE_FULLY_REMOVED: - case Intent.ACTION_PACKAGE_DATA_CLEARED: - String packageName = intent.getData().getSchemeSpecificPart(); - if (packageName == null) { - Log.e(TAG, "Package name is missing in the intent: " + intent); - return; - } - int uid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID); - if (uid == INVALID_UID) { - Log.e(TAG, "uid is missing in the intent: " + intent); - return; - } - handlePackageRemoved(packageName, uid); - break; - default: - Log.e(TAG, "Received unknown intent: " + intent); - } - } - } - - private void handlePackageRemoved(@NonNull String packageName, int uid) { - UserHandle userHandle = UserHandle.getUserHandleForUid(uid); - try { - if (isUserLocked(userHandle)) { - // We cannot access a locked user's directry and remove package data from it. - // We should remove those uninstalled package data when the user is unlocking. - return; - } - // Only clear the package's data if AppSearch exists for this user. - if (AppSearchUserInstanceManager.getAppSearchDir(userHandle).exists()) { - Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0); - AppSearchUserInstance instance = - mAppSearchUserInstanceManager.getOrCreateUserInstance( - userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); - //TODO(b/145759910) clear visibility setting for package. - instance.getAppSearchImpl().clearPackageData(packageName); - instance.getLogger().removeCachedUidForPackage(packageName); - } - } catch (Throwable t) { - Log.e(TAG, "Unable to remove data for package: " + packageName, t); - } - } - - @Override - public void onUserUnlocking(@NonNull TargetUser user) { - Objects.requireNonNull(user); - UserHandle userHandle = user.getUserHandle(); - synchronized (mUnlockedUsersLocked) { - mUnlockedUsersLocked.add(userHandle); - } - EXECUTOR.execute(() -> { - try { - // Only clear the package's data if AppSearch exists for this user. - if (AppSearchUserInstanceManager.getAppSearchDir(userHandle).exists()) { - Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0); - AppSearchUserInstance instance = - mAppSearchUserInstanceManager.getOrCreateUserInstance( - userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); - List installedPackageInfos = userContext - .getPackageManager() - .getInstalledPackages(/*flags=*/0); - Set packagesToKeep = new ArraySet<>(installedPackageInfos.size()); - for (int i = 0; i < installedPackageInfos.size(); i++) { - packagesToKeep.add(installedPackageInfos.get(i).packageName); - } - packagesToKeep.add(VisibilityStore.PACKAGE_NAME); - //TODO(b/145759910) clear visibility setting for package. - instance.getAppSearchImpl().prunePackageData(packagesToKeep); - } - } catch (Throwable t) { - Log.e(TAG, "Unable to prune packages for " + user, t); - } - }); - } - - @Override - public void onUserStopping(@NonNull TargetUser user) { - Objects.requireNonNull(user); - - synchronized (mUnlockedUsersLocked) { - UserHandle userHandle = user.getUserHandle(); - mUnlockedUsersLocked.remove(userHandle); - try { - mAppSearchUserInstanceManager.closeAndRemoveUserInstance(userHandle); - } catch (Throwable t) { - Log.e(TAG, "Error handling user stopping.", t); - } - } - } - - private void verifyUserUnlocked(@NonNull UserHandle callingUser) { - if (isUserLocked(callingUser)) { - throw new IllegalStateException(callingUser + " is locked or not running."); - } - } - - private boolean isUserLocked(@NonNull UserHandle callingUser) { - synchronized (mUnlockedUsersLocked) { - // First, check the local copy. - if (mUnlockedUsersLocked.contains(callingUser)) { - return false; - } - // If the local copy says the user is locked, check with UM for the actual state, - // since the user might just have been unlocked. - return !mUserManager.isUserUnlockingOrUnlocked(callingUser); - } - } - - private class Stub extends IAppSearchManager.Stub { - @Override - public void setSchema( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull List schemaBundles, - @NonNull List schemasNotDisplayedBySystem, - @NonNull Map> schemasVisibleToPackagesBundles, - boolean forceOverride, - int schemaVersion, - @NonNull UserHandle userHandle, - @ElapsedRealtimeLong long binderCallStartTimeMillis, - @NonNull IAppSearchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(schemaBundles); - Objects.requireNonNull(schemasNotDisplayedBySystem); - Objects.requireNonNull(schemasVisibleToPackagesBundles); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - AppSearchUserInstance instance = null; - int operationSuccessCount = 0; - int operationFailureCount = 0; - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - List schemas = new ArrayList<>(schemaBundles.size()); - for (int i = 0; i < schemaBundles.size(); i++) { - schemas.add(new AppSearchSchema(schemaBundles.get(i))); - } - Map> schemasVisibleToPackages = - new ArrayMap<>(schemasVisibleToPackagesBundles.size()); - for (Map.Entry> entry : - schemasVisibleToPackagesBundles.entrySet()) { - List packageIdentifiers = - new ArrayList<>(entry.getValue().size()); - for (int i = 0; i < entry.getValue().size(); i++) { - packageIdentifiers.add( - new PackageIdentifier(entry.getValue().get(i))); - } - schemasVisibleToPackages.put(entry.getKey(), packageIdentifiers); - } - instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); - SetSchemaResponse setSchemaResponse = instance.getAppSearchImpl().setSchema( - packageName, - databaseName, - schemas, - instance.getVisibilityStore(), - schemasNotDisplayedBySystem, - schemasVisibleToPackages, - forceOverride, - schemaVersion); - ++operationSuccessCount; - invokeCallbackOnResult(callback, - AppSearchResult.newSuccessfulResult(setSchemaResponse.getBundle())); - - // setSchema will sync the schemas in the request to AppSearch, any existing - // schemas which is not included in the request will be delete if we force - // override incompatible schemas. And all documents of these types will be - // deleted as well. We should checkForOptimize for these deletion. - checkForOptimize(instance); - } catch (Throwable t) { - ++operationFailureCount; - statusCode = throwableToFailedResult(t).getResultCode(); - invokeCallbackOnError(callback, t); - } finally { - if (instance != null) { - int estimatedBinderLatencyMillis = - 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); - int totalLatencyMillis = - (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - instance.getLogger().logStats(new CallStats.Builder() - .setPackageName(packageName) - .setDatabase(databaseName) - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis) - .setCallType(CallStats.CALL_TYPE_SET_SCHEMA) - // TODO(b/173532925) check the existing binder call latency chart - // is good enough for us: - // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 - .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) - .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount) - .build()); - } - } - }); - } - - @Override - public void getSchema( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull UserHandle userHandle, - @NonNull IAppSearchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - AppSearchUserInstance instance = - mAppSearchUserInstanceManager.getUserInstance(callingUser); - GetSchemaResponse response = - instance.getAppSearchImpl().getSchema(packageName, databaseName); - invokeCallbackOnResult( - callback, - AppSearchResult.newSuccessfulResult(response.getBundle())); - } catch (Throwable t) { - invokeCallbackOnError(callback, t); - } - }); - } - - @Override - public void getNamespaces( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull UserHandle userHandle, - @NonNull IAppSearchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - AppSearchUserInstance instance = - mAppSearchUserInstanceManager.getUserInstance(callingUser); - List namespaces = - instance.getAppSearchImpl().getNamespaces(packageName, databaseName); - invokeCallbackOnResult( - callback, AppSearchResult.newSuccessfulResult(namespaces)); - } catch (Throwable t) { - invokeCallbackOnError(callback, t); - } - }); - } - - @Override - public void putDocuments( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull List documentBundles, - @NonNull UserHandle userHandle, - @ElapsedRealtimeLong long binderCallStartTimeMillis, - @NonNull IAppSearchBatchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(documentBundles); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - AppSearchUserInstance instance = null; - int operationSuccessCount = 0; - int operationFailureCount = 0; - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - AppSearchBatchResult.Builder resultBuilder = - new AppSearchBatchResult.Builder<>(); - instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); - for (int i = 0; i < documentBundles.size(); i++) { - GenericDocument document = new GenericDocument(documentBundles.get(i)); - try { - instance.getAppSearchImpl().putDocument( - packageName, databaseName, document, instance.getLogger()); - resultBuilder.setSuccess(document.getId(), /*value=*/ null); - ++operationSuccessCount; - } catch (Throwable t) { - resultBuilder.setResult(document.getId(), throwableToFailedResult(t)); - AppSearchResult result = throwableToFailedResult(t); - resultBuilder.setResult(document.getId(), result); - // Since we can only include one status code in the atom, - // for failures, we would just save the one for the last failure - statusCode = result.getResultCode(); - ++operationFailureCount; - } - } - // Now that the batch has been written. Persist the newly written data. - instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE); - invokeCallbackOnResult(callback, resultBuilder.build()); - - // The existing documents with same ID will be deleted, so there may be some - // resources that could be released after optimize(). - checkForOptimize(instance, /*mutateBatchSize=*/ documentBundles.size()); - } catch (Throwable t) { - ++operationFailureCount; - statusCode = throwableToFailedResult(t).getResultCode(); - invokeCallbackOnError(callback, t); - } finally { - if (instance != null) { - int estimatedBinderLatencyMillis = - 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); - int totalLatencyMillis = - (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - instance.getLogger().logStats(new CallStats.Builder() - .setPackageName(packageName) - .setDatabase(databaseName) - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis) - .setCallType(CallStats.CALL_TYPE_PUT_DOCUMENTS) - // TODO(b/173532925) check the existing binder call latency chart - // is good enough for us: - // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 - .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) - .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount) - .build()); - } - } - }); - } - - @Override - public void getDocuments( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String namespace, - @NonNull List ids, - @NonNull Map> typePropertyPaths, - @NonNull UserHandle userHandle, - @ElapsedRealtimeLong long binderCallStartTimeMillis, - @NonNull IAppSearchBatchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(namespace); - Objects.requireNonNull(ids); - Objects.requireNonNull(typePropertyPaths); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - AppSearchUserInstance instance = null; - int operationSuccessCount = 0; - int operationFailureCount = 0; - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - AppSearchBatchResult.Builder resultBuilder = - new AppSearchBatchResult.Builder<>(); - instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); - for (int i = 0; i < ids.size(); i++) { - String id = ids.get(i); - try { - GenericDocument document = instance.getAppSearchImpl().getDocument( - packageName, - databaseName, - namespace, - id, - typePropertyPaths); - ++operationSuccessCount; - resultBuilder.setSuccess(id, document.getBundle()); - } catch (Throwable t) { - // Since we can only include one status code in the atom, - // for failures, we would just save the one for the last failure - AppSearchResult result = throwableToFailedResult(t); - resultBuilder.setResult(id, result); - statusCode = result.getResultCode(); - ++operationFailureCount; - } - } - invokeCallbackOnResult(callback, resultBuilder.build()); - } catch (Throwable t) { - ++operationFailureCount; - statusCode = throwableToFailedResult(t).getResultCode(); - invokeCallbackOnError(callback, t); - } finally { - if (instance != null) { - int estimatedBinderLatencyMillis = - 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); - int totalLatencyMillis = - (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - instance.getLogger().logStats(new CallStats.Builder() - .setPackageName(packageName) - .setDatabase(databaseName) - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis) - .setCallType(CallStats.CALL_TYPE_GET_DOCUMENTS) - // TODO(b/173532925) check the existing binder call latency chart - // is good enough for us: - // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 - .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) - .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount) - .build()); - } - } - }); - } - - @Override - public void query( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String queryExpression, - @NonNull Bundle searchSpecBundle, - @NonNull UserHandle userHandle, - @ElapsedRealtimeLong long binderCallStartTimeMillis, - @NonNull IAppSearchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(queryExpression); - Objects.requireNonNull(searchSpecBundle); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - AppSearchUserInstance instance = null; - int operationSuccessCount = 0; - int operationFailureCount = 0; - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); - SearchResultPage searchResultPage = instance.getAppSearchImpl().query( - packageName, - databaseName, - queryExpression, - new SearchSpec(searchSpecBundle), - instance.getLogger()); - ++operationSuccessCount; - invokeCallbackOnResult( - callback, - AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); - } catch (Throwable t) { - ++operationFailureCount; - statusCode = throwableToFailedResult(t).getResultCode(); - invokeCallbackOnError(callback, t); - } finally { - if (instance != null) { - int estimatedBinderLatencyMillis = - 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); - int totalLatencyMillis = - (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - instance.getLogger().logStats(new CallStats.Builder() - .setPackageName(packageName) - .setDatabase(databaseName) - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis) - .setCallType(CallStats.CALL_TYPE_SEARCH) - // TODO(b/173532925) check the existing binder call latency chart - // is good enough for us: - // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 - .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) - .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount) - .build()); - } - } - }); - } - - @Override - public void globalQuery( - @NonNull String packageName, - @NonNull String queryExpression, - @NonNull Bundle searchSpecBundle, - @NonNull UserHandle userHandle, - @ElapsedRealtimeLong long binderCallStartTimeMillis, - @NonNull IAppSearchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(queryExpression); - Objects.requireNonNull(searchSpecBundle); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - AppSearchUserInstance instance = null; - int operationSuccessCount = 0; - int operationFailureCount = 0; - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); - - boolean callerHasSystemAccess = - instance.getVisibilityStore().doesCallerHaveSystemAccess(packageName); - SearchResultPage searchResultPage = instance.getAppSearchImpl().globalQuery( - queryExpression, - new SearchSpec(searchSpecBundle), - packageName, - instance.getVisibilityStore(), - callingUid, - callerHasSystemAccess, - instance.getLogger()); - ++operationSuccessCount; - invokeCallbackOnResult( - callback, - AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); - } catch (Throwable t) { - ++operationFailureCount; - statusCode = throwableToFailedResult(t).getResultCode(); - invokeCallbackOnError(callback, t); - } finally { - if (instance != null) { - int estimatedBinderLatencyMillis = - 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); - int totalLatencyMillis = - (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - instance.getLogger().logStats(new CallStats.Builder() - .setPackageName(packageName) - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis) - .setCallType(CallStats.CALL_TYPE_GLOBAL_SEARCH) - // TODO(b/173532925) check the existing binder call latency chart - // is good enough for us: - // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 - .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) - .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount) - .build()); - } - } - }); - } - - @Override - public void getNextPage( - @NonNull String packageName, - long nextPageToken, - @NonNull UserHandle userHandle, - @NonNull IAppSearchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally - // opened it - EXECUTOR.execute(() -> { - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - AppSearchUserInstance instance = - mAppSearchUserInstanceManager.getUserInstance(callingUser); - SearchResultPage searchResultPage = - instance.getAppSearchImpl().getNextPage(packageName, nextPageToken); - invokeCallbackOnResult( - callback, - AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); - } catch (Throwable t) { - invokeCallbackOnError(callback, t); - } - }); - } - - @Override - public void invalidateNextPageToken(@NonNull String packageName, long nextPageToken, - @NonNull UserHandle userHandle) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(userHandle); - - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - AppSearchUserInstance instance = - mAppSearchUserInstanceManager.getUserInstance(callingUser); - instance.getAppSearchImpl().invalidateNextPageToken(packageName, nextPageToken); - } catch (Throwable t) { - Log.e(TAG, "Unable to invalidate the query page token", t); - } - }); - } - - @Override - public void writeQueryResultsToFile( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull ParcelFileDescriptor fileDescriptor, - @NonNull String queryExpression, - @NonNull Bundle searchSpecBundle, - @NonNull UserHandle userHandle, - @NonNull IAppSearchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(fileDescriptor); - Objects.requireNonNull(queryExpression); - Objects.requireNonNull(searchSpecBundle); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - AppSearchUserInstance instance = - mAppSearchUserInstanceManager.getUserInstance(callingUser); - // we don't need to append the file. The file is always brand new. - try (DataOutputStream outputStream = new DataOutputStream( - new FileOutputStream(fileDescriptor.getFileDescriptor()))) { - SearchResultPage searchResultPage = instance.getAppSearchImpl().query( - packageName, - databaseName, - queryExpression, - new SearchSpec(searchSpecBundle), - /*logger=*/ null); - while (!searchResultPage.getResults().isEmpty()) { - for (int i = 0; i < searchResultPage.getResults().size(); i++) { - AppSearchMigrationHelper.writeBundleToOutputStream( - outputStream, searchResultPage.getResults().get(i) - .getGenericDocument().getBundle()); - } - searchResultPage = instance.getAppSearchImpl().getNextPage( - packageName, searchResultPage.getNextPageToken()); - } - } - invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); - } catch (Throwable t) { - invokeCallbackOnError(callback, t); - } - }); - } - - @Override - public void putDocumentsFromFile( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull ParcelFileDescriptor fileDescriptor, - @NonNull UserHandle userHandle, - @NonNull IAppSearchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(fileDescriptor); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - AppSearchUserInstance instance = - mAppSearchUserInstanceManager.getUserInstance(callingUser); - - GenericDocument document; - ArrayList migrationFailureBundles = new ArrayList<>(); - try (DataInputStream inputStream = new DataInputStream( - new FileInputStream(fileDescriptor.getFileDescriptor()))) { - while (true) { - try { - document = AppSearchMigrationHelper - .readDocumentFromInputStream(inputStream); - } catch (EOFException e) { - // nothing wrong, we just finish the reading. - break; - } - try { - instance.getAppSearchImpl().putDocument( - packageName, databaseName, document, /*logger=*/ null); - } catch (Throwable t) { - migrationFailureBundles.add(new SetSchemaResponse.MigrationFailure( - document.getNamespace(), - document.getId(), - document.getSchemaType(), - AppSearchResult.throwableToFailedResult(t)) - .getBundle()); - } - } - } - instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL); - invokeCallbackOnResult(callback, - AppSearchResult.newSuccessfulResult(migrationFailureBundles)); - } catch (Throwable t) { - invokeCallbackOnError(callback, t); - } - }); - } - - @Override - public void reportUsage( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String namespace, - @NonNull String documentId, - long usageTimeMillis, - boolean systemUsage, - @NonNull UserHandle userHandle, - @NonNull IAppSearchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(namespace); - Objects.requireNonNull(documentId); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - AppSearchUserInstance instance = - mAppSearchUserInstanceManager.getUserInstance(callingUser); - - if (systemUsage - && !instance.getVisibilityStore() - .doesCallerHaveSystemAccess(packageName)) { - throw new AppSearchException( - AppSearchResult.RESULT_SECURITY_ERROR, - packageName + " does not have access to report system usage"); - } - - instance.getAppSearchImpl().reportUsage( - packageName, databaseName, namespace, documentId, - usageTimeMillis, systemUsage); - invokeCallbackOnResult( - callback, AppSearchResult.newSuccessfulResult(/*value=*/ null)); - } catch (Throwable t) { - invokeCallbackOnError(callback, t); - } - }); - } - - @Override - public void removeByDocumentId( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String namespace, - @NonNull List ids, - @NonNull UserHandle userHandle, - @ElapsedRealtimeLong long binderCallStartTimeMillis, - @NonNull IAppSearchBatchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(namespace); - Objects.requireNonNull(ids); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - AppSearchUserInstance instance = null; - int operationSuccessCount = 0; - int operationFailureCount = 0; - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - AppSearchBatchResult.Builder resultBuilder = - new AppSearchBatchResult.Builder<>(); - instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); - for (int i = 0; i < ids.size(); i++) { - String id = ids.get(i); - try { - instance.getAppSearchImpl().remove( - packageName, - databaseName, - namespace, - id, - /*removeStatsBuilder=*/ null); - ++operationSuccessCount; - resultBuilder.setSuccess(id, /*result= */ null); - } catch (Throwable t) { - AppSearchResult result = throwableToFailedResult(t); - resultBuilder.setResult(id, result); - // Since we can only include one status code in the atom, - // for failures, we would just save the one for the last failure - statusCode = result.getResultCode(); - ++operationFailureCount; - } - } - // Now that the batch has been written. Persist the newly written data. - instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE); - invokeCallbackOnResult(callback, resultBuilder.build()); - - checkForOptimize(instance, ids.size()); - } catch (Throwable t) { - ++operationFailureCount; - statusCode = throwableToFailedResult(t).getResultCode(); - invokeCallbackOnError(callback, t); - } finally { - if (instance != null) { - int estimatedBinderLatencyMillis = - 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); - int totalLatencyMillis = - (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - instance.getLogger().logStats(new CallStats.Builder() - .setPackageName(packageName) - .setDatabase(databaseName) - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis) - .setCallType(CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_ID) - // TODO(b/173532925) check the existing binder call latency chart - // is good enough for us: - // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 - .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) - .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount) - .build()); - } - } - }); - } - - @Override - public void removeByQuery( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String queryExpression, - @NonNull Bundle searchSpecBundle, - @NonNull UserHandle userHandle, - @ElapsedRealtimeLong long binderCallStartTimeMillis, - @NonNull IAppSearchResultCallback callback) { - // TODO(b/173532925) log CallStats once we have CALL_TYPE_REMOVE_BY_QUERY added - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(queryExpression); - Objects.requireNonNull(searchSpecBundle); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - AppSearchUserInstance instance = null; - int operationSuccessCount = 0; - int operationFailureCount = 0; - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); - instance.getAppSearchImpl().removeByQuery( - packageName, - databaseName, - queryExpression, - new SearchSpec(searchSpecBundle), - /*removeStatsBuilder=*/ null); - // Now that the batch has been written. Persist the newly written data. - instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE); - ++operationSuccessCount; - invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); - - checkForOptimize(instance); - } catch (Throwable t) { - ++operationFailureCount; - statusCode = throwableToFailedResult(t).getResultCode(); - invokeCallbackOnError(callback, t); - } finally { - if (instance != null) { - int estimatedBinderLatencyMillis = - 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); - int totalLatencyMillis = - (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - instance.getLogger().logStats(new CallStats.Builder() - .setPackageName(packageName) - .setDatabase(databaseName) - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis) - .setCallType(CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH) - // TODO(b/173532925) check the existing binder call latency chart - // is good enough for us: - // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 - .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) - .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount) - .build()); - } - } - }); - } - - @Override - public void getStorageInfo( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull UserHandle userHandle, - @NonNull IAppSearchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - AppSearchUserInstance instance = - mAppSearchUserInstanceManager.getUserInstance(callingUser); - StorageInfo storageInfo = instance.getAppSearchImpl() - .getStorageInfoForDatabase(packageName, databaseName); - Bundle storageInfoBundle = storageInfo.getBundle(); - invokeCallbackOnResult( - callback, AppSearchResult.newSuccessfulResult(storageInfoBundle)); - } catch (Throwable t) { - invokeCallbackOnError(callback, t); - } - }); - } - - @Override - public void persistToDisk( - @NonNull String packageName, - @NonNull UserHandle userHandle, - @ElapsedRealtimeLong long binderCallStartTimeMillis) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(userHandle); - - long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - EXECUTOR.execute(() -> { - @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - AppSearchUserInstance instance = null; - int operationSuccessCount = 0; - int operationFailureCount = 0; - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - instance = mAppSearchUserInstanceManager.getUserInstance(callingUser); - instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL); - ++operationSuccessCount; - } catch (Throwable t) { - ++operationFailureCount; - statusCode = throwableToFailedResult(t).getResultCode(); - Log.e(TAG, "Unable to persist the data to disk", t); - } finally { - if (instance != null) { - int estimatedBinderLatencyMillis = - 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); - int totalLatencyMillis = - (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - instance.getLogger().logStats(new CallStats.Builder() - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis) - .setCallType(CallStats.CALL_TYPE_FLUSH) - // TODO(b/173532925) check the existing binder call latency chart - // is good enough for us: - // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 - .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) - .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount) - .build()); - } - } - }); - } - - @Override - public void initialize( - @NonNull String packageName, - @NonNull UserHandle userHandle, - @ElapsedRealtimeLong long binderCallStartTimeMillis, - @NonNull IAppSearchResultCallback callback) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(callback); - - long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); - int callingUid = Binder.getCallingUid(); - UserHandle callingUser = handleIncomingUser(userHandle, callingUid); - - EXECUTOR.execute(() -> { - @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; - AppSearchUserInstance instance = null; - int operationSuccessCount = 0; - int operationFailureCount = 0; - try { - Context userContext = mContext.createContextAsUser(callingUser, /*flags=*/ 0); - verifyUserUnlocked(callingUser); - verifyCallingPackage(userContext, callingUser, callingUid, packageName); - verifyNotInstantApp(userContext, packageName); - instance = mAppSearchUserInstanceManager.getOrCreateUserInstance( - userContext, callingUser, AppSearchConfig.getInstance(EXECUTOR)); - ++operationSuccessCount; - invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); - } catch (Throwable t) { - ++operationFailureCount; - statusCode = throwableToFailedResult(t).getResultCode(); - invokeCallbackOnError(callback, t); - } finally { - if (instance != null) { - int estimatedBinderLatencyMillis = - 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); - int totalLatencyMillis = - (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); - instance.getLogger().logStats(new CallStats.Builder() - .setStatusCode(statusCode) - .setTotalLatencyMillis(totalLatencyMillis) - .setCallType(CallStats.CALL_TYPE_INITIALIZE) - // TODO(b/173532925) check the existing binder call latency chart - // is good enough for us: - // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 - .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) - .setNumOperationsSucceeded(operationSuccessCount) - .setNumOperationsFailed(operationFailureCount) - .build()); - } - } - }); - } - - private void verifyCallingPackage( - @NonNull Context userContext, - @NonNull UserHandle actualCallingUser, - int actualCallingUid, - @NonNull String claimedCallingPackage) { - Objects.requireNonNull(actualCallingUser); - Objects.requireNonNull(claimedCallingPackage); - - int claimedCallingUid = PackageUtil.getPackageUid( - userContext, claimedCallingPackage); - if (claimedCallingUid == INVALID_UID) { - throw new SecurityException( - "Specified calling package [" + claimedCallingPackage + "] not found"); - } - if (claimedCallingUid != actualCallingUid) { - throw new SecurityException( - "Specified calling package [" - + claimedCallingPackage - + "] does not match the calling uid " - + actualCallingUid); - } - } - - /** Invokes the {@link IAppSearchResultCallback} with the result. */ - private void invokeCallbackOnResult( - IAppSearchResultCallback callback, AppSearchResult result) { - try { - callback.onResult(new AppSearchResultParcel<>(result)); - } catch (RemoteException e) { - Log.e(TAG, "Unable to send result to the callback", e); - } - } - - /** Invokes the {@link IAppSearchBatchResultCallback} with the result. */ - private void invokeCallbackOnResult( - IAppSearchBatchResultCallback callback, AppSearchBatchResult result) { - try { - callback.onResult(new AppSearchBatchResultParcel<>(result)); - } catch (RemoteException e) { - Log.e(TAG, "Unable to send result to the callback", e); - } - } - - /** - * Invokes the {@link IAppSearchResultCallback} with an throwable. - * - *

The throwable is convert to a {@link AppSearchResult}; - */ - private void invokeCallbackOnError(IAppSearchResultCallback callback, Throwable throwable) { - AppSearchResult result = throwableToFailedResult(throwable); - try { - callback.onResult(new AppSearchResultParcel<>(result)); - } catch (RemoteException e) { - Log.e(TAG, "Unable to send result to the callback", e); - } - } - - /** - * Invokes the {@link IAppSearchBatchResultCallback} with an unexpected internal throwable. - * - *

The throwable is converted to {@link AppSearchResult}. - */ - private void invokeCallbackOnError( - @NonNull IAppSearchBatchResultCallback callback, @NonNull Throwable throwable) { - AppSearchResult result = throwableToFailedResult(throwable); - try { - callback.onSystemError(new AppSearchResultParcel<>(result)); - } catch (RemoteException e) { - Log.e(TAG, "Unable to send error to the callback", e); - } - } - } - - /** - * Helper for dealing with incoming user arguments to system service calls. - * - * @param requestedUser The user which the caller is requesting to execute as. - * @param callingUid The actual uid of the caller as determined by Binder. - * @return the user handle that the call should run as. Will always be a concrete user. - */ - @NonNull - private UserHandle handleIncomingUser(@NonNull UserHandle requestedUser, int callingUid) { - UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid); - if (callingUser.equals(requestedUser)) { - return requestedUser; - } - - // Duplicates UserController#ensureNotSpecialUser - if (requestedUser.getIdentifier() < 0) { - throw new IllegalArgumentException( - "Call does not support special user " + requestedUser); - } - - throw new SecurityException( - "Requested user, " + requestedUser + ", is not the same as the calling user, " - + callingUser + "."); - } - - /** - * Helper for ensuring instant apps can't make calls to AppSearch. - * - * @param userContext Context of the user making the call. - * @param packageName Package name of the caller. - * @throws SecurityException if the caller is an instant app. - */ - private void verifyNotInstantApp(@NonNull Context userContext, @NonNull String packageName) { - PackageManager callingPackageManager = userContext.getPackageManager(); - if (callingPackageManager.isInstantApp(packageName)) { - throw new SecurityException("Caller not allowed to create AppSearch session" - + "; userHandle=" + userContext.getUser() + ", callingPackage=" + packageName); - } - } - - // TODO(b/179160886): Cache the previous storage stats. - private class AppSearchStorageStatsAugmenter implements StorageStatsAugmenter { - @Override - public void augmentStatsForPackageForUser( - @NonNull PackageStats stats, - @NonNull String packageName, - @NonNull UserHandle userHandle, - boolean canCallerAccessAllStats) { - Objects.requireNonNull(stats); - Objects.requireNonNull(packageName); - Objects.requireNonNull(userHandle); - - try { - verifyUserUnlocked(userHandle); - Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0); - AppSearchUserInstance instance = - mAppSearchUserInstanceManager.getOrCreateUserInstance( - userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); - stats.dataSize += instance.getAppSearchImpl() - .getStorageInfoForPackage(packageName).getSizeBytes(); - } catch (Throwable t) { - Log.e( - TAG, - "Unable to augment storage stats for " - + userHandle - + " packageName " - + packageName, - t); - } - } - - @Override - public void augmentStatsForUid( - @NonNull PackageStats stats, int uid, boolean canCallerAccessAllStats) { - Objects.requireNonNull(stats); - - UserHandle userHandle = UserHandle.getUserHandleForUid(uid); - try { - verifyUserUnlocked(userHandle); - String[] packagesForUid = mPackageManager.getPackagesForUid(uid); - if (packagesForUid == null) { - return; - } - Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0); - AppSearchUserInstance instance = - mAppSearchUserInstanceManager.getOrCreateUserInstance( - userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); - for (int i = 0; i < packagesForUid.length; i++) { - stats.dataSize += instance.getAppSearchImpl() - .getStorageInfoForPackage(packagesForUid[i]).getSizeBytes(); - } - } catch (Throwable t) { - Log.e(TAG, "Unable to augment storage stats for uid " + uid, t); - } - } - - @Override - public void augmentStatsForUser( - @NonNull PackageStats stats, @NonNull UserHandle userHandle) { - // TODO(b/179160886): this implementation could incur many jni calls and a lot of - // in-memory processing from getStorageInfoForPackage. Instead, we can just compute the - // size of the icing dir (or use the overall StorageInfo without interpolating it). - Objects.requireNonNull(stats); - Objects.requireNonNull(userHandle); - - try { - verifyUserUnlocked(userHandle); - List packagesForUser = mPackageManager.getInstalledPackagesAsUser( - /*flags=*/0, userHandle.getIdentifier()); - if (packagesForUser == null) { - return; - } - Context userContext = mContext.createContextAsUser(userHandle, /*flags=*/ 0); - AppSearchUserInstance instance = - mAppSearchUserInstanceManager.getOrCreateUserInstance( - userContext, userHandle, AppSearchConfig.getInstance(EXECUTOR)); - for (int i = 0; i < packagesForUser.size(); i++) { - String packageName = packagesForUser.get(i).packageName; - stats.dataSize += instance.getAppSearchImpl() - .getStorageInfoForPackage(packageName).getSizeBytes(); - } - } catch (Throwable t) { - Log.e(TAG, "Unable to augment storage stats for " + userHandle, t); - } - } - } - - private void checkForOptimize(AppSearchUserInstance instance, int mutateBatchSize) { - EXECUTOR.execute(() -> { - long totalLatencyStartMillis = SystemClock.elapsedRealtime(); - OptimizeStats.Builder builder = new OptimizeStats.Builder(); - try { - instance.getAppSearchImpl().checkForOptimize(mutateBatchSize, builder); - } catch (AppSearchException e) { - Log.w(TAG, "Error occurred when check for optimize", e); - } finally { - OptimizeStats oStats = builder - .setTotalLatencyMillis( - (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)) - .build(); - if (oStats.getOriginalDocumentCount() > 0) { - // see if optimize has been run by checking originalDocumentCount - instance.getLogger().logStats(oStats); - } - } - }); - } - - private void checkForOptimize(AppSearchUserInstance instance) { - EXECUTOR.execute(() -> { - long totalLatencyStartMillis = SystemClock.elapsedRealtime(); - OptimizeStats.Builder builder = new OptimizeStats.Builder(); - try { - instance.getAppSearchImpl().checkForOptimize(builder); - } catch (AppSearchException e) { - Log.w(TAG, "Error occurred when check for optimize", e); - } finally { - OptimizeStats oStats = builder - .setTotalLatencyMillis( - (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)) - .build(); - if (oStats.getOriginalDocumentCount() > 0) { - // see if optimize has been run by checking originalDocumentCount - instance.getLogger().logStats(oStats); - } - } - }); - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java deleted file mode 100644 index 56e2af5fa2e31..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstance.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.AppSearchImpl; -import com.android.server.appsearch.stats.PlatformLogger; -import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl; - -import java.util.Objects; - -/** - * Container for AppSearch classes that should only be initialized once per device-user and make up - * the core of the AppSearch system. - */ -public final class AppSearchUserInstance { - private final PlatformLogger mLogger; - private final AppSearchImpl mAppSearchImpl; - private final VisibilityStoreImpl mVisibilityStore; - - AppSearchUserInstance( - @NonNull PlatformLogger logger, - @NonNull AppSearchImpl appSearchImpl, - @NonNull VisibilityStoreImpl visibilityStore) { - mLogger = Objects.requireNonNull(logger); - mAppSearchImpl = Objects.requireNonNull(appSearchImpl); - mVisibilityStore = Objects.requireNonNull(visibilityStore); - } - - @NonNull - public PlatformLogger getLogger() { - return mLogger; - } - - @NonNull - public AppSearchImpl getAppSearchImpl() { - return mAppSearchImpl; - } - - @NonNull - public VisibilityStoreImpl getVisibilityStore() { - return mVisibilityStore; - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java deleted file mode 100644 index 529f2b04f600e..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2019 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 android.app.appsearch.exceptions.AppSearchException; -import android.content.Context; -import android.os.Environment; -import android.os.SystemClock; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.server.appsearch.external.localstorage.AppSearchImpl; -import com.android.server.appsearch.external.localstorage.stats.InitializeStats; -import com.android.server.appsearch.stats.PlatformLogger; -import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * Manages the lifecycle of AppSearch classes that should only be initialized once per device-user - * and make up the core of the AppSearch system. - * - * @hide - */ -public final class AppSearchUserInstanceManager { - private static final String TAG = "AppSearchUserInstanceMa"; - - private static volatile AppSearchUserInstanceManager sAppSearchUserInstanceManager; - - @GuardedBy("mInstancesLocked") - private final Map mInstancesLocked = new ArrayMap<>(); - - private AppSearchUserInstanceManager() {} - - /** - * Gets an instance of AppSearchUserInstanceManager to be used. - * - *

If no instance has been initialized yet, a new one will be created. Otherwise, the - * existing instance will be returned. - */ - @NonNull - public static AppSearchUserInstanceManager getInstance() { - if (sAppSearchUserInstanceManager == null) { - synchronized (AppSearchUserInstanceManager.class) { - if (sAppSearchUserInstanceManager == null) { - sAppSearchUserInstanceManager = new AppSearchUserInstanceManager(); - } - } - } - return sAppSearchUserInstanceManager; - } - - /** - * Returns AppSearch directory in the credential encrypted system directory for the given user. - * - *

This folder should only be accessed after unlock. - */ - public static File getAppSearchDir(@NonNull UserHandle userHandle) { - // Duplicates the implementation of Environment#getDataSystemCeDirectory - // TODO(b/191059409): Unhide Environment#getDataSystemCeDirectory and switch to it. - File systemCeDir = new File(Environment.getDataDirectory(), "system_ce"); - File systemCeUserDir = new File(systemCeDir, String.valueOf(userHandle.getIdentifier())); - return new File(systemCeUserDir, "appsearch"); - } - - /** - * Gets an instance of AppSearchUserInstance for the given user, or creates one if none exists. - * - *

If no AppSearchUserInstance exists for the unlocked user, Icing will be initialized and - * one will be created. - * - * @param userContext Context of the user calling AppSearch - * @param userHandle The multi-user handle of the device user calling AppSearch - * @param config Flag manager for AppSearch - * @return An initialized {@link AppSearchUserInstance} for this user - */ - @NonNull - public AppSearchUserInstance getOrCreateUserInstance( - @NonNull Context userContext, - @NonNull UserHandle userHandle, - @NonNull AppSearchConfig config) - throws AppSearchException { - Objects.requireNonNull(userContext); - Objects.requireNonNull(userHandle); - Objects.requireNonNull(config); - - synchronized (mInstancesLocked) { - AppSearchUserInstance instance = mInstancesLocked.get(userHandle); - if (instance == null) { - instance = createUserInstance(userContext, userHandle, config); - mInstancesLocked.put(userHandle, instance); - } - return instance; - } - } - - /** - * Closes and removes an {@link AppSearchUserInstance} for the given user. - * - *

All mutations applied to the underlying {@link AppSearchImpl} will be persisted to disk. - * - * @param userHandle The multi-user user handle of the user that need to be removed. - */ - public void closeAndRemoveUserInstance(@NonNull UserHandle userHandle) { - Objects.requireNonNull(userHandle); - synchronized (mInstancesLocked) { - AppSearchUserInstance instance = mInstancesLocked.remove(userHandle); - if (instance != null) { - instance.getAppSearchImpl().close(); - } - } - } - - /** - * Gets an {@link AppSearchUserInstance} for the given user. - * - *

This method should only be called by an initialized SearchSession, which has already - * called {@link #getOrCreateUserInstance} before. - * - * @param userHandle The multi-user handle of the device user calling AppSearch - * @return An initialized {@link AppSearchUserInstance} for this user - * @throws IllegalStateException if {@link AppSearchUserInstance} haven't created for the given - * user. - */ - @NonNull - public AppSearchUserInstance getUserInstance(@NonNull UserHandle userHandle) { - Objects.requireNonNull(userHandle); - synchronized (mInstancesLocked) { - AppSearchUserInstance instance = mInstancesLocked.get(userHandle); - if (instance == null) { - // Impossible scenario, user cannot call an uninitialized SearchSession, - // getInstance should always find the instance for the given user and never try to - // create an instance for this user again. - throw new IllegalStateException( - "AppSearchUserInstance has never been created for: " + userHandle); - } - return instance; - } - } - - /** - * Returns the list of all {@link UserHandle}s. - * - *

It can return an empty list if there is no {@link AppSearchUserInstance} created yet. - */ - @NonNull - public List getAllUserHandles() { - synchronized (mInstancesLocked) { - return new ArrayList<>(mInstancesLocked.keySet()); - } - } - - @NonNull - private AppSearchUserInstance createUserInstance( - @NonNull Context userContext, - @NonNull UserHandle userHandle, - @NonNull AppSearchConfig config) - throws AppSearchException { - long totalLatencyStartMillis = SystemClock.elapsedRealtime(); - InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder(); - - // Initialize the classes that make up AppSearchUserInstance - PlatformLogger logger = new PlatformLogger(userContext, config); - - File appSearchDir = getAppSearchDir(userHandle); - File icingDir = new File(appSearchDir, "icing"); - Log.i(TAG, "Creating new AppSearch instance at: " + icingDir); - AppSearchImpl appSearchImpl = AppSearchImpl.create( - icingDir, - new FrameworkLimitConfig(config), - initStatsBuilder, - new FrameworkOptimizeStrategy(config)); - - long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime(); - VisibilityStoreImpl visibilityStore = - VisibilityStoreImpl.create(appSearchImpl, userContext); - long prepareVisibilityStoreLatencyEndMillis = SystemClock.elapsedRealtime(); - - initStatsBuilder - .setTotalLatencyMillis( - (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)) - .setPrepareVisibilityStoreLatencyMillis( - (int) - (prepareVisibilityStoreLatencyEndMillis - - prepareVisibilityStoreLatencyStartMillis)); - logger.logStats(initStatsBuilder.build()); - - return new AppSearchUserInstance(logger, appSearchImpl, 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 deleted file mode 100644 index d16168a915d58..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/FrameworkLimitConfig.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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/FrameworkOptimizeStrategy.java b/apex/appsearch/service/java/com/android/server/appsearch/FrameworkOptimizeStrategy.java deleted file mode 100644 index d9344493ac348..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/FrameworkOptimizeStrategy.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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.AppSearchImpl; -import com.android.server.appsearch.external.localstorage.OptimizeStrategy; - -import com.google.android.icing.proto.GetOptimizeInfoResultProto; - -import java.util.Objects; - -/** - * An implementation of {@link OptimizeStrategy} will determine when to trigger {@link - * AppSearchImpl#optimize()} in Jetpack environment. - * - * @hide - */ -public class FrameworkOptimizeStrategy implements OptimizeStrategy { - private final AppSearchConfig mAppSearchConfig; - FrameworkOptimizeStrategy(@NonNull AppSearchConfig config) { - mAppSearchConfig = Objects.requireNonNull(config); - } - - @Override - public boolean shouldOptimize(@NonNull GetOptimizeInfoResultProto optimizeInfo) { - return optimizeInfo.getOptimizableDocs() - >= mAppSearchConfig.getCachedDocCountOptimizeThreshold() - || optimizeInfo.getEstimatedOptimizableBytes() - >= mAppSearchConfig.getCachedBytesOptimizeThreshold() - || optimizeInfo.getTimeSinceLastOptimizeMs() - >= mAppSearchConfig.getCachedTimeOptimizeThresholdMs(); - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING b/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING deleted file mode 100644 index 38cd7a83359ae..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/TEST_MAPPING +++ /dev/null @@ -1,26 +0,0 @@ -{ - "presubmit": [ - { - "name": "CtsAppSearchTestCases" - }, - { - "name": "CtsAppSearchHostTestCases" - }, - { - "name": "FrameworksServicesTests", - "options": [ - { - "include-filter": "com.android.server.appsearch" - } - ] - }, - { - "name": "FrameworksCoreTests", - "options": [ - { - "include-filter": "android.app.appsearch" - } - ] - } - ] -} 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 deleted file mode 100644 index 15916cc23c0f6..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +++ /dev/null @@ -1,2299 +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 com.android.server.appsearch.external.localstorage; - -import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.addPrefixToDocument; -import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix; -import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getDatabaseName; -import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getPackageName; -import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getPrefix; -import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.removePrefix; -import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.removePrefixesFromDocument; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.WorkerThread; -import android.app.appsearch.AppSearchResult; -import android.app.appsearch.AppSearchSchema; -import android.app.appsearch.GenericDocument; -import android.app.appsearch.GetByDocumentIdRequest; -import android.app.appsearch.GetSchemaResponse; -import android.app.appsearch.PackageIdentifier; -import android.app.appsearch.SearchResultPage; -import android.app.appsearch.SearchSpec; -import android.app.appsearch.SetSchemaResponse; -import android.app.appsearch.StorageInfo; -import android.app.appsearch.exceptions.AppSearchException; -import android.app.appsearch.util.LogUtil; -import android.os.Bundle; -import android.os.SystemClock; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.Log; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter; -import com.android.server.appsearch.external.localstorage.converter.ResultCodeToProtoConverter; -import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter; -import com.android.server.appsearch.external.localstorage.converter.SearchResultToProtoConverter; -import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter; -import com.android.server.appsearch.external.localstorage.converter.SetSchemaResponseToProtoConverter; -import com.android.server.appsearch.external.localstorage.converter.TypePropertyPathToProtoConverter; -import com.android.server.appsearch.external.localstorage.stats.InitializeStats; -import com.android.server.appsearch.external.localstorage.stats.OptimizeStats; -import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; -import com.android.server.appsearch.external.localstorage.stats.RemoveStats; -import com.android.server.appsearch.external.localstorage.stats.SearchStats; -import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore; - -import com.google.android.icing.IcingSearchEngine; -import com.google.android.icing.proto.DeleteByQueryResultProto; -import com.google.android.icing.proto.DeleteResultProto; -import com.google.android.icing.proto.DocumentProto; -import com.google.android.icing.proto.DocumentStorageInfoProto; -import com.google.android.icing.proto.GetAllNamespacesResultProto; -import com.google.android.icing.proto.GetOptimizeInfoResultProto; -import com.google.android.icing.proto.GetResultProto; -import com.google.android.icing.proto.GetResultSpecProto; -import com.google.android.icing.proto.GetSchemaResultProto; -import com.google.android.icing.proto.IcingSearchEngineOptions; -import com.google.android.icing.proto.InitializeResultProto; -import com.google.android.icing.proto.NamespaceStorageInfoProto; -import com.google.android.icing.proto.OptimizeResultProto; -import com.google.android.icing.proto.PersistToDiskResultProto; -import com.google.android.icing.proto.PersistType; -import com.google.android.icing.proto.PropertyConfigProto; -import com.google.android.icing.proto.PutResultProto; -import com.google.android.icing.proto.ReportUsageResultProto; -import com.google.android.icing.proto.ResetResultProto; -import com.google.android.icing.proto.ResultSpecProto; -import com.google.android.icing.proto.SchemaProto; -import com.google.android.icing.proto.SchemaTypeConfigProto; -import com.google.android.icing.proto.ScoringSpecProto; -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; - -import java.io.Closeable; -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -/** - * Manages interaction with the native IcingSearchEngine and other components to implement AppSearch - * functionality. - * - *

Never create two instances using the same folder. - * - *

A single instance of {@link AppSearchImpl} can support all packages and databases. This is - * done by combining the package and database name into a unique prefix and prefixing the schemas - * and documents stored under that owner. Schemas and documents are physically saved together in - * {@link IcingSearchEngine}, but logically isolated: - * - *

    - *
  • Rewrite SchemaType in SchemaProto by adding the package-database prefix and save into - * SchemaTypes set in {@link #setSchema}. - *
  • Rewrite namespace and SchemaType in DocumentProto by adding package-database prefix and - * save to namespaces set in {@link #putDocument}. - *
  • Remove package-database prefix when retrieving documents in {@link #getDocument} and {@link - * #query}. - *
  • Rewrite filters in {@link SearchSpecProto} to have all namespaces and schema types of the - * queried database when user using empty filters in {@link #query}. - *
- * - *

Methods in this class belong to two groups, the query group and the mutate group. - * - *

    - *
  • All methods are going to modify global parameters and data in Icing are executed under - * WRITE lock to keep thread safety. - *
  • All methods are going to access global parameters or query data from Icing are executed - * under READ lock to improve query performance. - *
- * - *

This class is thread safe. - * - * @hide - */ -@WorkerThread -public final class AppSearchImpl implements Closeable { - private static final String TAG = "AppSearchImpl"; - - /** A value 0 means that there're no more pages in the search results. */ - private static final long EMPTY_PAGE_TOKEN = 0; - - @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 - final IcingSearchEngine mIcingSearchEngineLocked; - - // This map contains schema types and SchemaTypeConfigProtos for all package-database - // prefixes. It maps each package-database prefix to an inner-map. The inner-map maps each - // prefixed schema type to its respective SchemaTypeConfigProto. - @GuardedBy("mReadWriteLock") - private final Map> mSchemaMapLocked = - new ArrayMap<>(); - - // This map contains namespaces for all package-database prefixes. All values in the map are - // prefixed with the package-database prefix. - // TODO(b/172360376): Check if this can be replaced with an ArrayMap - @GuardedBy("mReadWriteLock") - private final Map> mNamespaceMapLocked = new HashMap<>(); - - /** Maps package name to active document count. */ - @GuardedBy("mReadWriteLock") - private final Map mDocumentCountMapLocked = new ArrayMap<>(); - - // Maps packages to the set of valid nextPageTokens that the package can manipulate. A token - // is unique and constant per query (i.e. the same token '123' is used to iterate through - // pages of search results). The tokens themselves are generated and tracked by - // IcingSearchEngine. IcingSearchEngine considers a token valid and won't be reused - // until we call invalidateNextPageToken on the token. - // - // Note that we synchronize on itself because the nextPageToken cache is checked at - // query-time, and queries are done in parallel with a read lock. Ideally, this would be - // guarded by the normal mReadWriteLock.writeLock, but ReentrantReadWriteLocks can't upgrade - // read to write locks. This lock should be acquired at the smallest scope possible. - // mReadWriteLock is a higher-level lock, so calls shouldn't be made out - // to any functions that grab the lock. - @GuardedBy("mNextPageTokensLocked") - private final Map> mNextPageTokensLocked = new ArrayMap<>(); - - /** - * The counter to check when to call {@link #checkForOptimize}. The interval is {@link - * #CHECK_OPTIMIZE_INTERVAL}. - */ - @GuardedBy("mReadWriteLock") - private int mOptimizeIntervalCountLocked = 0; - - /** Whether this instance has been closed, and therefore unusable. */ - @GuardedBy("mReadWriteLock") - private boolean mClosedLocked = false; - - /** - * Creates and initializes an instance of {@link AppSearchImpl} which writes data to the given - * folder. - * - *

Clients can pass a {@link AppSearchLogger} here through their AppSearchSession, but it - * can't be saved inside {@link AppSearchImpl}, because the impl will be shared by all the - * sessions for the same package in JetPack. - * - *

Instead, logger instance needs to be passed to each individual method, like create, query - * and putDocument. - * - * @param initStatsBuilder collects stats for initialization if provided. - */ - @NonNull - public static AppSearchImpl create( - @NonNull File icingDir, - @NonNull LimitConfig limitConfig, - @Nullable InitializeStats.Builder initStatsBuilder, - @NonNull OptimizeStrategy optimizeStrategy) - throws AppSearchException { - 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(); - try { - // We synchronize here because we don't want to call IcingSearchEngine.initialize() more - // than once. It's unnecessary and can be a costly operation. - IcingSearchEngineOptions options = - IcingSearchEngineOptions.newBuilder() - .setBaseDir(icingDir.getAbsolutePath()) - .build(); - mLogUtil.piiTrace("Constructing IcingSearchEngine, request", options); - mIcingSearchEngineLocked = new IcingSearchEngine(options); - mLogUtil.piiTrace( - "Constructing IcingSearchEngine, response", - Objects.hashCode(mIcingSearchEngineLocked)); - - // 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(); - mLogUtil.piiTrace( - "icingSearchEngine.initialize, response", - initializeResultProto.getStatus(), - initializeResultProto); - - if (initStatsBuilder != null) { - initStatsBuilder - .setStatusCode( - statusProtoToResultCode(initializeResultProto.getStatus())) - // TODO(b/173532925) how to get DeSyncs value - .setHasDeSync(false); - 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(); - - mLogUtil.piiTrace("init:getAllNamespaces, request"); - GetAllNamespacesResultProto getAllNamespacesResultProto = - mIcingSearchEngineLocked.getAllNamespaces(); - mLogUtil.piiTrace( - "init:getAllNamespaces, response", - getAllNamespacesResultProto.getNamespacesCount(), - getAllNamespacesResultProto); - - StorageInfoProto storageInfoProto = getRawStorageInfoProto(); - - // Log the time it took to read the data that goes into the cache maps - if (initStatsBuilder != null) { - // In case there is some error for getAllNamespaces, we can still - // set the latency for preparation. - // If there is no error, the value will be overridden by the actual one later. - initStatsBuilder - .setStatusCode( - statusProtoToResultCode( - getAllNamespacesResultProto.getStatus())) - .setPrepareSchemaAndNamespacesLatencyMillis( - (int) - (SystemClock.elapsedRealtime() - - prepareSchemaAndNamespacesLatencyStartMillis)); - } - checkSuccess(getAllNamespacesResultProto.getStatus()); - - // Populate schema map - List 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 - List 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( - (int) - (SystemClock.elapsedRealtime() - - 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 { - mReadWriteLock.writeLock().unlock(); - } - } - - @GuardedBy("mReadWriteLock") - private void throwIfClosedLocked() { - if (mClosedLocked) { - throw new IllegalStateException("Trying to use a closed AppSearchImpl instance."); - } - } - - /** - * Persists data to disk and closes the instance. - * - *

This instance is no longer usable after it's been closed. Call {@link #create} to create a - * new, usable instance. - */ - @Override - public void close() { - mReadWriteLock.writeLock().lock(); - try { - if (mClosedLocked) { - return; - } - persistToDisk(PersistType.Code.FULL); - mLogUtil.piiTrace("icingSearchEngine.close, request"); - mIcingSearchEngineLocked.close(); - mLogUtil.piiTrace("icingSearchEngine.close, response"); - mClosedLocked = true; - } catch (AppSearchException e) { - Log.w(TAG, "Error when closing AppSearchImpl.", e); - } finally { - mReadWriteLock.writeLock().unlock(); - } - } - - /** - * Updates the AppSearch schema for this app. - * - *

This method belongs to mutate group. - * - * @param packageName The package name that owns the schemas. - * @param databaseName The name of the database where this schema lives. - * @param schemas Schemas to set for this app. - * @param visibilityStore If set, {@code schemasNotDisplayedBySystem} and {@code - * schemasVisibleToPackages} will be saved here if the schema is successfully applied. - * @param schemasNotDisplayedBySystem Schema types that should not be surfaced on platform - * surfaces. - * @param schemasVisibleToPackages Schema types that are visible to the specified packages. - * @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents - * which do not comply with the new schema will be deleted. - * @param version The overall version number of the request. - * @return The response contains deleted schema types and incompatible schema types of this - * call. - * @throws AppSearchException On IcingSearchEngine error. If the status code is - * FAILED_PRECONDITION for the incompatible change, the exception will be converted to the - * SetSchemaResponse. - */ - @NonNull - public SetSchemaResponse setSchema( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull List schemas, - @Nullable VisibilityStore visibilityStore, - @NonNull List schemasNotDisplayedBySystem, - @NonNull Map> schemasVisibleToPackages, - boolean forceOverride, - int version) - throws AppSearchException { - mReadWriteLock.writeLock().lock(); - try { - throwIfClosedLocked(); - - SchemaProto.Builder existingSchemaBuilder = getSchemaProtoLocked().toBuilder(); - - SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder(); - for (int i = 0; i < schemas.size(); i++) { - AppSearchSchema schema = schemas.get(i); - SchemaTypeConfigProto schemaTypeProto = - SchemaToProtoConverter.toSchemaTypeConfigProto(schema, version); - newSchemaBuilder.addTypes(schemaTypeProto); - } - - String prefix = createPrefix(packageName, databaseName); - // Combine the existing schema (which may have types from other prefixes) with this - // prefix's new schema. Modifies the existingSchemaBuilder. - RewrittenSchemaResults rewrittenSchemaResults = - rewriteSchema(prefix, existingSchemaBuilder, newSchemaBuilder.build()); - - // Apply schema - SchemaProto finalSchema = existingSchemaBuilder.build(); - mLogUtil.piiTrace("setSchema, request", finalSchema.getTypesCount(), finalSchema); - SetSchemaResultProto setSchemaResultProto = - mIcingSearchEngineLocked.setSchema(finalSchema, forceOverride); - mLogUtil.piiTrace( - "setSchema, response", setSchemaResultProto.getStatus(), setSchemaResultProto); - - // Determine whether it succeeded. - try { - checkSuccess(setSchemaResultProto.getStatus()); - } catch (AppSearchException e) { - // Swallow the exception for the incompatible change case. We will propagate - // those deleted schemas and incompatible types to the SetSchemaResponse. - boolean isFailedPrecondition = - setSchemaResultProto.getStatus().getCode() - == StatusProto.Code.FAILED_PRECONDITION; - boolean isIncompatible = - setSchemaResultProto.getDeletedSchemaTypesCount() > 0 - || setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0; - if (isFailedPrecondition && isIncompatible) { - return SetSchemaResponseToProtoConverter.toSetSchemaResponse( - setSchemaResultProto, prefix); - } else { - throw e; - } - } - - // Update derived data structures. - for (SchemaTypeConfigProto schemaTypeConfigProto : - rewrittenSchemaResults.mRewrittenPrefixedTypes.values()) { - addToMap(mSchemaMapLocked, prefix, schemaTypeConfigProto); - } - - for (String schemaType : rewrittenSchemaResults.mDeletedPrefixedTypes) { - removeFromMap(mSchemaMapLocked, prefix, schemaType); - } - - if (visibilityStore != null) { - Set prefixedSchemasNotDisplayedBySystem = - new ArraySet<>(schemasNotDisplayedBySystem.size()); - for (int i = 0; i < schemasNotDisplayedBySystem.size(); i++) { - prefixedSchemasNotDisplayedBySystem.add( - prefix + schemasNotDisplayedBySystem.get(i)); - } - - Map> prefixedSchemasVisibleToPackages = - new ArrayMap<>(schemasVisibleToPackages.size()); - for (Map.Entry> entry : - schemasVisibleToPackages.entrySet()) { - prefixedSchemasVisibleToPackages.put(prefix + entry.getKey(), entry.getValue()); - } - - visibilityStore.setVisibility( - packageName, - databaseName, - prefixedSchemasNotDisplayedBySystem, - prefixedSchemasVisibleToPackages); - } - - return SetSchemaResponseToProtoConverter.toSetSchemaResponse( - setSchemaResultProto, prefix); - } finally { - mReadWriteLock.writeLock().unlock(); - } - } - - /** - * Retrieves the AppSearch schema for this package name, database. - * - *

This method belongs to query group. - * - * @param packageName Package name that owns this schema - * @param databaseName The name of the database where this schema lives. - * @throws AppSearchException on IcingSearchEngine error. - */ - @NonNull - public GetSchemaResponse getSchema(@NonNull String packageName, @NonNull String databaseName) - throws AppSearchException { - mReadWriteLock.readLock().lock(); - try { - throwIfClosedLocked(); - - SchemaProto fullSchema = getSchemaProtoLocked(); - - String prefix = createPrefix(packageName, databaseName); - GetSchemaResponse.Builder responseBuilder = new GetSchemaResponse.Builder(); - for (int i = 0; i < fullSchema.getTypesCount(); i++) { - String typePrefix = getPrefix(fullSchema.getTypes(i).getSchemaType()); - if (!prefix.equals(typePrefix)) { - continue; - } - // Rewrite SchemaProto.types.schema_type - SchemaTypeConfigProto.Builder typeConfigBuilder = - fullSchema.getTypes(i).toBuilder(); - String newSchemaType = typeConfigBuilder.getSchemaType().substring(prefix.length()); - typeConfigBuilder.setSchemaType(newSchemaType); - - // Rewrite SchemaProto.types.properties.schema_type - for (int propertyIdx = 0; - propertyIdx < typeConfigBuilder.getPropertiesCount(); - propertyIdx++) { - PropertyConfigProto.Builder propertyConfigBuilder = - typeConfigBuilder.getProperties(propertyIdx).toBuilder(); - if (!propertyConfigBuilder.getSchemaType().isEmpty()) { - String newPropertySchemaType = - propertyConfigBuilder.getSchemaType().substring(prefix.length()); - propertyConfigBuilder.setSchemaType(newPropertySchemaType); - typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder); - } - } - - AppSearchSchema schema = - SchemaToProtoConverter.toAppSearchSchema(typeConfigBuilder); - - // TODO(b/183050495) find a place to store the version for the database, rather - // than read from a schema. - responseBuilder.setVersion(fullSchema.getTypes(i).getVersion()); - responseBuilder.addSchema(schema); - } - return responseBuilder.build(); - } finally { - mReadWriteLock.readLock().unlock(); - } - } - - /** - * Retrieves the list of namespaces with at least one document for this package name, database. - * - *

This method belongs to query group. - * - * @param packageName Package name that owns this schema - * @param databaseName The name of the database where this schema lives. - * @throws AppSearchException on IcingSearchEngine error. - */ - @NonNull - public List getNamespaces(@NonNull String packageName, @NonNull String databaseName) - throws AppSearchException { - mReadWriteLock.readLock().lock(); - try { - throwIfClosedLocked(); - mLogUtil.piiTrace("getAllNamespaces, request"); - // 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 - // using deleteByQuery). - GetAllNamespacesResultProto getAllNamespacesResultProto = - mIcingSearchEngineLocked.getAllNamespaces(); - mLogUtil.piiTrace( - "getAllNamespaces, response", - getAllNamespacesResultProto.getNamespacesCount(), - getAllNamespacesResultProto); - checkSuccess(getAllNamespacesResultProto.getStatus()); - String prefix = createPrefix(packageName, databaseName); - List results = new ArrayList<>(); - for (int i = 0; i < getAllNamespacesResultProto.getNamespacesCount(); i++) { - String prefixedNamespace = getAllNamespacesResultProto.getNamespaces(i); - if (prefixedNamespace.startsWith(prefix)) { - results.add(prefixedNamespace.substring(prefix.length())); - } - } - return results; - } finally { - mReadWriteLock.readLock().unlock(); - } - } - - /** - * Adds a document to the AppSearch index. - * - *

This method belongs to mutate group. - * - * @param packageName The package name that owns this document. - * @param databaseName The databaseName this document resides in. - * @param document The document to index. - * @throws AppSearchException on IcingSearchEngine error. - */ - public void putDocument( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull GenericDocument document, - @Nullable AppSearchLogger logger) - throws AppSearchException { - PutDocumentStats.Builder pStatsBuilder = null; - if (logger != null) { - pStatsBuilder = new PutDocumentStats.Builder(packageName, databaseName); - } - long totalStartTimeMillis = SystemClock.elapsedRealtime(); - - mReadWriteLock.writeLock().lock(); - try { - throwIfClosedLocked(); - - // Generate Document Proto - long generateDocumentProtoStartTimeMillis = SystemClock.elapsedRealtime(); - DocumentProto.Builder documentBuilder = - GenericDocumentToProtoConverter.toDocumentProto(document).toBuilder(); - long generateDocumentProtoEndTimeMillis = SystemClock.elapsedRealtime(); - - // Rewrite Document Type - long rewriteDocumentTypeStartTimeMillis = SystemClock.elapsedRealtime(); - String prefix = createPrefix(packageName, databaseName); - addPrefixToDocument(documentBuilder, prefix); - long rewriteDocumentTypeEndTimeMillis = SystemClock.elapsedRealtime(); - 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(finalDocument); - mLogUtil.piiTrace("putDocument, response", putResultProto.getStatus(), putResultProto); - - // Update caches - addToMap(mNamespaceMapLocked, prefix, finalDocument.getNamespace()); - mDocumentCountMapLocked.put(packageName, newDocumentCount); - - // Logging stats - if (pStatsBuilder != null) { - pStatsBuilder - .setStatusCode(statusProtoToResultCode(putResultProto.getStatus())) - .setGenerateDocumentProtoLatencyMillis( - (int) - (generateDocumentProtoEndTimeMillis - - generateDocumentProtoStartTimeMillis)) - .setRewriteDocumentTypesLatencyMillis( - (int) - (rewriteDocumentTypeEndTimeMillis - - rewriteDocumentTypeStartTimeMillis)); - AppSearchLoggerHelper.copyNativeStats( - putResultProto.getPutDocumentStats(), pStatsBuilder); - } - - checkSuccess(putResultProto.getStatus()); - } finally { - mReadWriteLock.writeLock().unlock(); - - if (logger != null) { - long totalEndTimeMillis = SystemClock.elapsedRealtime(); - pStatsBuilder.setTotalLatencyMillis( - (int) (totalEndTimeMillis - totalStartTimeMillis)); - logger.logStats(pStatsBuilder.build()); - } - } - } - - /** - * 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. - * - *

This method belongs to query group. - * - * @param packageName The package that owns this document. - * @param databaseName The databaseName this document resides in. - * @param namespace The namespace this document resides in. - * @param id The ID of the document to get. - * @param typePropertyPaths A map of schema type to a list of property paths to return in the - * result. - * @return The Document contents - * @throws AppSearchException on IcingSearchEngine error. - */ - @NonNull - public GenericDocument getDocument( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String namespace, - @NonNull String id, - @NonNull Map> typePropertyPaths) - throws AppSearchException { - mReadWriteLock.readLock().lock(); - try { - throwIfClosedLocked(); - String prefix = createPrefix(packageName, databaseName); - List nonPrefixedPropertyMasks = - TypePropertyPathToProtoConverter.toTypePropertyMaskList(typePropertyPaths); - List prefixedPropertyMasks = - new ArrayList<>(nonPrefixedPropertyMasks.size()); - for (int i = 0; i < nonPrefixedPropertyMasks.size(); ++i) { - TypePropertyMask typePropertyMask = nonPrefixedPropertyMasks.get(i); - String nonPrefixedType = typePropertyMask.getSchemaType(); - String prefixedType = - nonPrefixedType.equals( - GetByDocumentIdRequest.PROJECTION_SCHEMA_TYPE_WILDCARD) - ? nonPrefixedType - : prefix + nonPrefixedType; - prefixedPropertyMasks.add( - typePropertyMask.toBuilder().setSchemaType(prefixedType).build()); - } - GetResultSpecProto getResultSpec = - GetResultSpecProto.newBuilder() - .addAllTypePropertyMasks(prefixedPropertyMasks) - .build(); - - String finalNamespace = createPrefix(packageName, databaseName) + namespace; - if (mLogUtil.isPiiTraceEnabled()) { - mLogUtil.piiTrace( - "getDocument, request", finalNamespace + ", " + id + "," + getResultSpec); - } - GetResultProto getResultProto = - mIcingSearchEngineLocked.get(finalNamespace, id, getResultSpec); - mLogUtil.piiTrace("getDocument, response", getResultProto.getStatus(), getResultProto); - checkSuccess(getResultProto.getStatus()); - - // The schema type map cannot be null at this point. It could only be null if no - // schema had ever been set for that prefix. Given we have retrieved a document from - // the index, we know a schema had to have been set. - Map schemaTypeMap = mSchemaMapLocked.get(prefix); - DocumentProto.Builder documentBuilder = getResultProto.getDocument().toBuilder(); - removePrefixesFromDocument(documentBuilder); - return GenericDocumentToProtoConverter.toGenericDocument( - documentBuilder.build(), prefix, schemaTypeMap); - } finally { - mReadWriteLock.readLock().unlock(); - } - } - - /** - * Executes a query against the AppSearch index and returns results. - * - *

This method belongs to query group. - * - * @param packageName The package name that is performing the query. - * @param databaseName The databaseName this query for. - * @param queryExpression Query String to search. - * @param searchSpec Spec for setting filters, raw query etc. - * @param logger logger to collect query stats - * @return The results of performing this search. It may contain an empty list of results if no - * documents matched the query. - * @throws AppSearchException on IcingSearchEngine error. - */ - @NonNull - public SearchResultPage query( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String queryExpression, - @NonNull SearchSpec searchSpec, - @Nullable AppSearchLogger logger) - throws AppSearchException { - long totalLatencyStartMillis = SystemClock.elapsedRealtime(); - SearchStats.Builder sStatsBuilder = null; - if (logger != null) { - sStatsBuilder = - new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_LOCAL, packageName) - .setDatabase(databaseName); - } - - mReadWriteLock.readLock().lock(); - try { - throwIfClosedLocked(); - - List filterPackageNames = searchSpec.getFilterPackageNames(); - if (!filterPackageNames.isEmpty() && !filterPackageNames.contains(packageName)) { - // Client wanted to query over some packages that weren't its own. This isn't - // allowed through local query so we can return early with no results. - if (logger != null) { - sStatsBuilder.setStatusCode(AppSearchResult.RESULT_SECURITY_ERROR); - } - return new SearchResultPage(Bundle.EMPTY); - } - - String prefix = createPrefix(packageName, databaseName); - Set allowedPrefixedSchemas = getAllowedPrefixSchemasLocked(prefix, searchSpec); - - SearchResultPage searchResultPage = - doQueryLocked( - Collections.singleton(createPrefix(packageName, databaseName)), - allowedPrefixedSchemas, - queryExpression, - searchSpec, - sStatsBuilder); - addNextPageToken(packageName, searchResultPage.getNextPageToken()); - return searchResultPage; - } finally { - mReadWriteLock.readLock().unlock(); - if (logger != null) { - sStatsBuilder.setTotalLatencyMillis( - (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)); - logger.logStats(sStatsBuilder.build()); - } - } - } - - /** - * Executes a global query, i.e. over all permitted prefixes, against the AppSearch index and - * returns results. - * - *

This method belongs to query group. - * - * @param queryExpression Query String to search. - * @param searchSpec Spec for setting filters, raw query etc. - * @param callerPackageName Package name of the caller, should belong to the {@code - * callerUserHandle}. - * @param visibilityStore Optional visibility store to obtain system and package visibility - * settings from - * @param callerUid UID of the client making the globalQuery call. - * @param callerHasSystemAccess Whether the caller has been positively identified as having - * access to schemas marked system surfaceable. - * @param logger logger to collect globalQuery stats - * @return The results of performing this search. It may contain an empty list of results if no - * documents matched the query. - * @throws AppSearchException on IcingSearchEngine error. - */ - @NonNull - public SearchResultPage globalQuery( - @NonNull String queryExpression, - @NonNull SearchSpec searchSpec, - @NonNull String callerPackageName, - @Nullable VisibilityStore visibilityStore, - int callerUid, - boolean callerHasSystemAccess, - @Nullable AppSearchLogger logger) - throws AppSearchException { - long totalLatencyStartMillis = SystemClock.elapsedRealtime(); - SearchStats.Builder sStatsBuilder = null; - if (logger != null) { - sStatsBuilder = - new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_GLOBAL, callerPackageName); - } - - mReadWriteLock.readLock().lock(); - try { - throwIfClosedLocked(); - - // Convert package filters to prefix filters - Set packageFilters = new ArraySet<>(searchSpec.getFilterPackageNames()); - Set prefixFilters = new ArraySet<>(); - if (packageFilters.isEmpty()) { - // Client didn't restrict their search over packages. Try to query over all - // packages/prefixes - prefixFilters = mNamespaceMapLocked.keySet(); - } else { - // Client did restrict their search over packages. Only include the prefixes that - // belong to the specified packages. - for (String prefix : mNamespaceMapLocked.keySet()) { - String packageName = getPackageName(prefix); - if (packageFilters.contains(packageName)) { - prefixFilters.add(prefix); - } - } - } - - // Convert schema filters to prefixed schema filters - ArraySet prefixedSchemaFilters = new ArraySet<>(); - for (String prefix : prefixFilters) { - List schemaFilters = searchSpec.getFilterSchemas(); - if (schemaFilters.isEmpty()) { - // Client didn't specify certain schemas to search over, check all schemas - prefixedSchemaFilters.addAll(mSchemaMapLocked.get(prefix).keySet()); - } else { - // Client specified some schemas to search over, check each one - for (int i = 0; i < schemaFilters.size(); i++) { - prefixedSchemaFilters.add(prefix + schemaFilters.get(i)); - } - } - } - - // Remove the schemas the client is not allowed to search over - Iterator prefixedSchemaIt = prefixedSchemaFilters.iterator(); - while (prefixedSchemaIt.hasNext()) { - String prefixedSchema = prefixedSchemaIt.next(); - String packageName = getPackageName(prefixedSchema); - - boolean allow; - if (packageName.equals(callerPackageName)) { - // Callers can always retrieve their own data - allow = true; - } else if (visibilityStore == null) { - // If there's no visibility store, there's no extra access - allow = false; - } else { - String databaseName = getDatabaseName(prefixedSchema); - allow = - visibilityStore.isSchemaSearchableByCaller( - packageName, - databaseName, - prefixedSchema, - callerUid, - callerHasSystemAccess); - } - - if (!allow) { - prefixedSchemaIt.remove(); - } - } - - SearchResultPage searchResultPage = - doQueryLocked( - prefixFilters, - prefixedSchemaFilters, - queryExpression, - searchSpec, - sStatsBuilder); - addNextPageToken(callerPackageName, searchResultPage.getNextPageToken()); - return searchResultPage; - } finally { - mReadWriteLock.readLock().unlock(); - - if (logger != null) { - sStatsBuilder.setTotalLatencyMillis( - (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)); - logger.logStats(sStatsBuilder.build()); - } - } - } - - /** - * Returns a mapping of package names to all the databases owned by that package. - * - *

This method is inefficient to call repeatedly. - */ - @NonNull - public Map> getPackageToDatabases() { - mReadWriteLock.readLock().lock(); - try { - Map> packageToDatabases = new ArrayMap<>(); - for (String prefix : mSchemaMapLocked.keySet()) { - String packageName = getPackageName(prefix); - - Set databases = packageToDatabases.get(packageName); - if (databases == null) { - databases = new ArraySet<>(); - packageToDatabases.put(packageName, databases); - } - - String databaseName = getDatabaseName(prefix); - databases.add(databaseName); - } - - return packageToDatabases; - } finally { - mReadWriteLock.readLock().unlock(); - } - } - - @GuardedBy("mReadWriteLock") - private SearchResultPage doQueryLocked( - @NonNull Set prefixes, - @NonNull Set allowedPrefixedSchemas, - @NonNull String queryExpression, - @NonNull SearchSpec searchSpec, - @Nullable SearchStats.Builder sStatsBuilder) - throws AppSearchException { - long rewriteSearchSpecLatencyStartMillis = SystemClock.elapsedRealtime(); - - SearchSpecProto.Builder searchSpecBuilder = - SearchSpecToProtoConverter.toSearchSpecProto(searchSpec).toBuilder() - .setQuery(queryExpression); - // rewriteSearchSpecForPrefixesLocked will return false if there is nothing to search - // over given their search filters, so we can return an empty SearchResult and skip - // sending request to Icing. - if (!rewriteSearchSpecForPrefixesLocked( - searchSpecBuilder, prefixes, allowedPrefixedSchemas)) { - if (sStatsBuilder != null) { - sStatsBuilder.setRewriteSearchSpecLatencyMillis( - (int) - (SystemClock.elapsedRealtime() - - rewriteSearchSpecLatencyStartMillis)); - } - return new SearchResultPage(Bundle.EMPTY); - } - - // rewriteSearchSpec, rewriteResultSpec and convertScoringSpec are all counted in - // rewriteSearchSpecLatencyMillis - ResultSpecProto.Builder resultSpecBuilder = - SearchSpecToProtoConverter.toResultSpecProto(searchSpec).toBuilder(); - - int groupingType = searchSpec.getResultGroupingTypeFlags(); - if ((groupingType & SearchSpec.GROUPING_TYPE_PER_PACKAGE) != 0 - && (groupingType & SearchSpec.GROUPING_TYPE_PER_NAMESPACE) != 0) { - addPerPackagePerNamespaceResultGroupingsLocked( - resultSpecBuilder, prefixes, searchSpec.getResultGroupingLimit()); - } else if ((groupingType & SearchSpec.GROUPING_TYPE_PER_PACKAGE) != 0) { - addPerPackageResultGroupingsLocked( - resultSpecBuilder, prefixes, searchSpec.getResultGroupingLimit()); - } else if ((groupingType & SearchSpec.GROUPING_TYPE_PER_NAMESPACE) != 0) { - addPerNamespaceResultGroupingsLocked( - resultSpecBuilder, prefixes, searchSpec.getResultGroupingLimit()); - } - - rewriteResultSpecForPrefixesLocked(resultSpecBuilder, prefixes, allowedPrefixedSchemas); - ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec); - SearchSpecProto finalSearchSpec = searchSpecBuilder.build(); - ResultSpecProto finalResultSpec = resultSpecBuilder.build(); - - long rewriteSearchSpecLatencyEndMillis = SystemClock.elapsedRealtime(); - - if (mLogUtil.isPiiTraceEnabled()) { - mLogUtil.piiTrace( - "search, request", - finalSearchSpec.getQuery(), - finalSearchSpec + ", " + scoringSpec + ", " + finalResultSpec); - } - SearchResultProto searchResultProto = - mIcingSearchEngineLocked.search(finalSearchSpec, scoringSpec, finalResultSpec); - mLogUtil.piiTrace( - "search, response", searchResultProto.getResultsCount(), searchResultProto); - - if (sStatsBuilder != null) { - sStatsBuilder - .setStatusCode(statusProtoToResultCode(searchResultProto.getStatus())) - .setRewriteSearchSpecLatencyMillis( - (int) - (rewriteSearchSpecLatencyEndMillis - - rewriteSearchSpecLatencyStartMillis)); - AppSearchLoggerHelper.copyNativeStats(searchResultProto.getQueryStats(), sStatsBuilder); - } - - checkSuccess(searchResultProto.getStatus()); - - long rewriteSearchResultLatencyStartMillis = SystemClock.elapsedRealtime(); - SearchResultPage resultPage = rewriteSearchResultProto(searchResultProto, mSchemaMapLocked); - if (sStatsBuilder != null) { - sStatsBuilder.setRewriteSearchResultLatencyMillis( - (int) (SystemClock.elapsedRealtime() - rewriteSearchResultLatencyStartMillis)); - } - - return resultPage; - } - - /** - * Fetches the next page of results of a previously executed query. Results can be empty if - * next-page token is invalid or all pages have been returned. - * - *

This method belongs to query group. - * - * @param packageName Package name of the caller. - * @param nextPageToken The token of pre-loaded results of previously executed query. - * @return The next page of results of previously executed query. - * @throws AppSearchException on IcingSearchEngine error or if can't advance on nextPageToken. - */ - @NonNull - public SearchResultPage getNextPage(@NonNull String packageName, long nextPageToken) - throws AppSearchException { - mReadWriteLock.readLock().lock(); - try { - throwIfClosedLocked(); - - mLogUtil.piiTrace("getNextPage, request", nextPageToken); - checkNextPageToken(packageName, nextPageToken); - SearchResultProto searchResultProto = - mIcingSearchEngineLocked.getNextPage(nextPageToken); - mLogUtil.piiTrace( - "getNextPage, response", - searchResultProto.getResultsCount(), - searchResultProto); - checkSuccess(searchResultProto.getStatus()); - if (nextPageToken != EMPTY_PAGE_TOKEN - && searchResultProto.getNextPageToken() == EMPTY_PAGE_TOKEN) { - // At this point, we're guaranteed that this nextPageToken exists for this package, - // otherwise checkNextPageToken would've thrown an exception. - // Since the new token is 0, this is the last page. We should remove the old token - // from our cache since it no longer refers to this query. - synchronized (mNextPageTokensLocked) { - mNextPageTokensLocked.get(packageName).remove(nextPageToken); - } - } - return rewriteSearchResultProto(searchResultProto, mSchemaMapLocked); - } finally { - mReadWriteLock.readLock().unlock(); - } - } - - /** - * Invalidates the next-page token so that no more results of the related query can be returned. - * - *

This method belongs to query group. - * - * @param packageName Package name of the caller. - * @param nextPageToken The token of pre-loaded results of previously executed query to be - * Invalidated. - * @throws AppSearchException if nextPageToken is unusable. - */ - public void invalidateNextPageToken(@NonNull String packageName, long nextPageToken) - throws AppSearchException { - mReadWriteLock.readLock().lock(); - try { - throwIfClosedLocked(); - - mLogUtil.piiTrace("invalidateNextPageToken, request", nextPageToken); - checkNextPageToken(packageName, nextPageToken); - mIcingSearchEngineLocked.invalidateNextPageToken(nextPageToken); - - synchronized (mNextPageTokensLocked) { - // At this point, we're guaranteed that this nextPageToken exists for this package, - // otherwise checkNextPageToken would've thrown an exception. - mNextPageTokensLocked.get(packageName).remove(nextPageToken); - } - } finally { - mReadWriteLock.readLock().unlock(); - } - } - - /** Reports a usage of the given document at the given timestamp. */ - public void reportUsage( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String namespace, - @NonNull String documentId, - long usageTimestampMillis, - boolean systemUsage) - throws AppSearchException { - mReadWriteLock.writeLock().lock(); - try { - throwIfClosedLocked(); - - String prefixedNamespace = createPrefix(packageName, databaseName) + namespace; - UsageReport.UsageType usageType = - systemUsage - ? UsageReport.UsageType.USAGE_TYPE2 - : UsageReport.UsageType.USAGE_TYPE1; - UsageReport report = - UsageReport.newBuilder() - .setDocumentNamespace(prefixedNamespace) - .setDocumentUri(documentId) - .setUsageTimestampMs(usageTimestampMillis) - .setUsageType(usageType) - .build(); - - mLogUtil.piiTrace("reportUsage, request", report.getDocumentUri(), report); - ReportUsageResultProto result = mIcingSearchEngineLocked.reportUsage(report); - mLogUtil.piiTrace("reportUsage, response", result.getStatus(), result); - checkSuccess(result.getStatus()); - } finally { - mReadWriteLock.writeLock().unlock(); - } - } - - /** - * Removes the given document by id. - * - *

This method belongs to mutate group. - * - * @param packageName The package name that owns the document. - * @param databaseName The databaseName the document is in. - * @param namespace Namespace of the document to remove. - * @param id ID of the document to remove. - * @param removeStatsBuilder builder for {@link RemoveStats} to hold stats for remove - * @throws AppSearchException on IcingSearchEngine error. - */ - public void remove( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String namespace, - @NonNull String id, - @Nullable RemoveStats.Builder removeStatsBuilder) - throws AppSearchException { - long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); - mReadWriteLock.writeLock().lock(); - try { - throwIfClosedLocked(); - - String prefixedNamespace = createPrefix(packageName, databaseName) + namespace; - if (mLogUtil.isPiiTraceEnabled()) { - mLogUtil.piiTrace("removeById, request", prefixedNamespace + ", " + id); - } - DeleteResultProto deleteResultProto = - mIcingSearchEngineLocked.delete(prefixedNamespace, id); - mLogUtil.piiTrace( - "removeById, response", deleteResultProto.getStatus(), deleteResultProto); - - if (removeStatsBuilder != null) { - removeStatsBuilder.setStatusCode( - statusProtoToResultCode(deleteResultProto.getStatus())); - AppSearchLoggerHelper.copyNativeStats( - deleteResultProto.getDeleteStats(), removeStatsBuilder); - } - checkSuccess(deleteResultProto.getStatus()); - - // Update derived maps - updateDocumentCountAfterRemovalLocked(packageName, /*numDocumentsDeleted=*/ 1); - } finally { - mReadWriteLock.writeLock().unlock(); - if (removeStatsBuilder != null) { - removeStatsBuilder.setTotalLatencyMillis( - (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis)); - } - } - } - - /** - * Removes documents by given query. - * - *

This method belongs to mutate group. - * - * @param packageName The package name that owns the documents. - * @param databaseName The databaseName the document is in. - * @param queryExpression Query String to search. - * @param searchSpec Defines what and how to remove - * @param removeStatsBuilder builder for {@link RemoveStats} to hold stats for remove - * @throws AppSearchException on IcingSearchEngine error. - */ - public void removeByQuery( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String queryExpression, - @NonNull SearchSpec searchSpec, - @Nullable RemoveStats.Builder removeStatsBuilder) - throws AppSearchException { - long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); - mReadWriteLock.writeLock().lock(); - try { - throwIfClosedLocked(); - - List filterPackageNames = searchSpec.getFilterPackageNames(); - if (!filterPackageNames.isEmpty() && !filterPackageNames.contains(packageName)) { - // We're only removing documents within the parameter `packageName`. If we're not - // restricting our remove-query to this package name, then there's nothing for us to - // remove. - return; - } - - SearchSpecProto searchSpecProto = - SearchSpecToProtoConverter.toSearchSpecProto(searchSpec); - SearchSpecProto.Builder searchSpecBuilder = - searchSpecProto.toBuilder().setQuery(queryExpression); - - String prefix = createPrefix(packageName, databaseName); - Set allowedPrefixedSchemas = getAllowedPrefixSchemasLocked(prefix, searchSpec); - - // rewriteSearchSpecForPrefixesLocked will return false if there is nothing to search - // over given their search filters, so we can return early and skip sending request - // to Icing. - if (!rewriteSearchSpecForPrefixesLocked( - searchSpecBuilder, Collections.singleton(prefix), allowedPrefixedSchemas)) { - return; - } - SearchSpecProto finalSearchSpec = searchSpecBuilder.build(); - mLogUtil.piiTrace("removeByQuery, request", finalSearchSpec); - DeleteByQueryResultProto deleteResultProto = - mIcingSearchEngineLocked.deleteByQuery(finalSearchSpec); - mLogUtil.piiTrace( - "removeByQuery, response", deleteResultProto.getStatus(), deleteResultProto); - - if (removeStatsBuilder != null) { - removeStatsBuilder.setStatusCode( - statusProtoToResultCode(deleteResultProto.getStatus())); - // TODO(b/187206766) also log query stats here once IcingLib returns it - AppSearchLoggerHelper.copyNativeStats( - deleteResultProto.getDeleteStats(), removeStatsBuilder); - } - - // It seems that the caller wants to get success if the data matching the query is - // not in the DB because it was not there or was successfully deleted. - checkCodeOneOf( - deleteResultProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND); - - // Update derived maps - int numDocumentsDeleted = deleteResultProto.getDeleteStats().getNumDocumentsDeleted(); - updateDocumentCountAfterRemovalLocked(packageName, numDocumentsDeleted); - } finally { - mReadWriteLock.writeLock().unlock(); - if (removeStatsBuilder != null) { - removeStatsBuilder.setTotalLatencyMillis( - (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis)); - } - } - } - - @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) - throws AppSearchException { - mReadWriteLock.readLock().lock(); - try { - throwIfClosedLocked(); - - Map> packageToDatabases = getPackageToDatabases(); - Set databases = packageToDatabases.get(packageName); - if (databases == null) { - // Package doesn't exist, no storage info to report - return new StorageInfo.Builder().build(); - } - - // Accumulate all the namespaces we're interested in. - Set wantedPrefixedNamespaces = new ArraySet<>(); - for (String database : databases) { - Set prefixedNamespaces = - mNamespaceMapLocked.get(createPrefix(packageName, database)); - if (prefixedNamespaces != null) { - wantedPrefixedNamespaces.addAll(prefixedNamespaces); - } - } - if (wantedPrefixedNamespaces.isEmpty()) { - return new StorageInfo.Builder().build(); - } - - return getStorageInfoForNamespaces(getRawStorageInfoProto(), wantedPrefixedNamespaces); - } finally { - mReadWriteLock.readLock().unlock(); - } - } - - /** Estimates the storage usage info for a specific database in a package. */ - @NonNull - public StorageInfo getStorageInfoForDatabase( - @NonNull String packageName, @NonNull String databaseName) throws AppSearchException { - mReadWriteLock.readLock().lock(); - try { - throwIfClosedLocked(); - - Map> packageToDatabases = getPackageToDatabases(); - Set databases = packageToDatabases.get(packageName); - if (databases == null) { - // Package doesn't exist, no storage info to report - return new StorageInfo.Builder().build(); - } - if (!databases.contains(databaseName)) { - // Database doesn't exist, no storage info to report - return new StorageInfo.Builder().build(); - } - - Set wantedPrefixedNamespaces = - mNamespaceMapLocked.get(createPrefix(packageName, databaseName)); - if (wantedPrefixedNamespaces == null || wantedPrefixedNamespaces.isEmpty()) { - return new StorageInfo.Builder().build(); - } - - return getStorageInfoForNamespaces(getRawStorageInfoProto(), wantedPrefixedNamespaces); - } finally { - mReadWriteLock.readLock().unlock(); - } - } - - /** - * Returns the native storage info capsuled in {@link StorageInfoResultProto} directly from - * IcingSearchEngine. - */ - @NonNull - 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 prefixedNamespaces) { - if (!storageInfoProto.hasDocumentStorageInfo()) { - return new StorageInfo.Builder().build(); - } - - long totalStorageSize = storageInfoProto.getTotalStorageSize(); - DocumentStorageInfoProto documentStorageInfo = storageInfoProto.getDocumentStorageInfo(); - int totalDocuments = - documentStorageInfo.getNumAliveDocuments() - + documentStorageInfo.getNumExpiredDocuments(); - - if (totalStorageSize == 0 || totalDocuments == 0) { - // Maybe we can exit early and also avoid a divide by 0 error. - return new StorageInfo.Builder().build(); - } - - // Accumulate stats across the package's namespaces. - int aliveDocuments = 0; - int expiredDocuments = 0; - int aliveNamespaces = 0; - List namespaceStorageInfos = - documentStorageInfo.getNamespaceStorageInfoList(); - for (int i = 0; i < namespaceStorageInfos.size(); i++) { - NamespaceStorageInfoProto namespaceStorageInfo = namespaceStorageInfos.get(i); - // The namespace from icing lib is already the prefixed format - if (prefixedNamespaces.contains(namespaceStorageInfo.getNamespace())) { - if (namespaceStorageInfo.getNumAliveDocuments() > 0) { - aliveNamespaces++; - aliveDocuments += namespaceStorageInfo.getNumAliveDocuments(); - } - expiredDocuments += namespaceStorageInfo.getNumExpiredDocuments(); - } - } - int namespaceDocuments = aliveDocuments + expiredDocuments; - - // Since we don't have the exact size of all the documents, we do an estimation. Note - // that while the total storage takes into account schema, index, etc. in addition to - // documents, we'll only calculate the percentage based on number of documents a - // client has. - return new StorageInfo.Builder() - .setSizeBytes((long) (namespaceDocuments * 1.0 / totalDocuments * totalStorageSize)) - .setAliveDocumentsCount(aliveDocuments) - .setAliveNamespacesCount(aliveNamespaces) - .build(); - } - - /** - * Persists all update/delete requests to the disk. - * - *

If the app crashes after a call to PersistToDisk with {@link PersistType.Code#FULL}, Icing - * would be able to fully recover all data written up to this point without a costly recovery - * process. - * - *

If the app crashes after a call to PersistToDisk with {@link PersistType.Code#LITE}, Icing - * would trigger a costly recovery process in next initialization. After that, Icing would still - * be able to recover all written data - excepting Usage data. Usage data is only guaranteed to - * be safe after a call to PersistToDisk with {@link PersistType.Code#FULL} - * - *

If the app crashes after an update/delete request has been made, but before any call to - * PersistToDisk, then all data in Icing will be lost. - * - * @param persistType the amount of data to persist. {@link PersistType.Code#LITE} will only - * persist the minimal amount of data to ensure all data can be recovered. {@link - * PersistType.Code#FULL} will persist all data necessary to prevent data loss without - * needing data recovery. - * @throws AppSearchException on any error that AppSearch persist data to disk. - */ - public void persistToDisk(@NonNull PersistType.Code persistType) throws AppSearchException { - mReadWriteLock.writeLock().lock(); - try { - throwIfClosedLocked(); - - mLogUtil.piiTrace("persistToDisk, request", persistType); - PersistToDiskResultProto persistToDiskResultProto = - mIcingSearchEngineLocked.persistToDisk(persistType); - mLogUtil.piiTrace( - "persistToDisk, response", - persistToDiskResultProto.getStatus(), - persistToDiskResultProto); - checkSuccess(persistToDiskResultProto.getStatus()); - } finally { - mReadWriteLock.writeLock().unlock(); - } - } - - /** - * Remove all {@link AppSearchSchema}s and {@link GenericDocument}s under the given package. - * - * @param packageName The name of package to be removed. - * @throws AppSearchException if we cannot remove the data. - */ - public void clearPackageData(@NonNull String packageName) throws AppSearchException { - mReadWriteLock.writeLock().lock(); - try { - throwIfClosedLocked(); - Set existingPackages = getPackageToDatabases().keySet(); - if (existingPackages.contains(packageName)) { - existingPackages.remove(packageName); - prunePackageData(existingPackages); - } - } finally { - mReadWriteLock.writeLock().unlock(); - } - } - - /** - * Remove all {@link AppSearchSchema}s and {@link GenericDocument}s that doesn't belong to any - * of the given installed packages - * - * @param installedPackages The name of all installed package. - * @throws AppSearchException if we cannot remove the data. - */ - public void prunePackageData(@NonNull Set installedPackages) throws AppSearchException { - mReadWriteLock.writeLock().lock(); - try { - throwIfClosedLocked(); - Map> packageToDatabases = getPackageToDatabases(); - if (installedPackages.containsAll(packageToDatabases.keySet())) { - // No package got removed. We are good. - return; - } - - // Prune schema proto - SchemaProto existingSchema = getSchemaProtoLocked(); - SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder(); - for (int i = 0; i < existingSchema.getTypesCount(); i++) { - String packageName = getPackageName(existingSchema.getTypes(i).getSchemaType()); - if (installedPackages.contains(packageName)) { - newSchemaBuilder.addTypes(existingSchema.getTypes(i)); - } - } - - SchemaProto finalSchema = newSchemaBuilder.build(); - - // Apply schema, set force override to true to remove all schemas and documents that - // doesn't belong to any of these installed packages. - mLogUtil.piiTrace( - "clearPackageData.setSchema, request", - finalSchema.getTypesCount(), - finalSchema); - SetSchemaResultProto setSchemaResultProto = - mIcingSearchEngineLocked.setSchema( - finalSchema, /*ignoreErrorsAndDeleteDocuments=*/ true); - mLogUtil.piiTrace( - "clearPackageData.setSchema, response", - setSchemaResultProto.getStatus(), - setSchemaResultProto); - - // Determine whether it succeeded. - checkSuccess(setSchemaResultProto.getStatus()); - - // Prune cached maps - for (Map.Entry> entry : packageToDatabases.entrySet()) { - String packageName = entry.getKey(); - Set databaseNames = entry.getValue(); - if (!installedPackages.contains(packageName) && databaseNames != null) { - mDocumentCountMapLocked.remove(packageName); - synchronized (mNextPageTokensLocked) { - mNextPageTokensLocked.remove(packageName); - } - for (String databaseName : databaseNames) { - String removedPrefix = createPrefix(packageName, databaseName); - mSchemaMapLocked.remove(removedPrefix); - mNamespaceMapLocked.remove(removedPrefix); - } - } - } - // TODO(b/145759910) clear visibility setting for package. - } finally { - mReadWriteLock.writeLock().unlock(); - } - } - - /** - * Clears documents and schema across all packages and databaseNames. - * - *

This method belongs to mutate group. - * - * @throws AppSearchException on IcingSearchEngine error. - */ - @GuardedBy("mReadWriteLock") - private void resetLocked(@Nullable InitializeStats.Builder initStatsBuilder) - throws AppSearchException { - mLogUtil.piiTrace("icingSearchEngine.reset, request"); - ResetResultProto resetResultProto = mIcingSearchEngineLocked.reset(); - mLogUtil.piiTrace( - "icingSearchEngine.reset, response", - resetResultProto.getStatus(), - resetResultProto); - mOptimizeIntervalCountLocked = 0; - mSchemaMapLocked.clear(); - mNamespaceMapLocked.clear(); - mDocumentCountMapLocked.clear(); - synchronized (mNextPageTokensLocked) { - mNextPageTokensLocked.clear(); - } - if (initStatsBuilder != null) { - initStatsBuilder - .setHasReset(true) - .setResetStatusCode(statusProtoToResultCode(resetResultProto.getStatus())); - } - - checkSuccess(resetResultProto.getStatus()); - } - - @GuardedBy("mReadWriteLock") - private void rebuildDocumentCountMapLocked(@NonNull StorageInfoProto storageInfoProto) { - mDocumentCountMapLocked.clear(); - List 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 { - // Any prefixed types that used to exist in the schema, but are deleted in the new one. - final Set mDeletedPrefixedTypes = new ArraySet<>(); - - // Map of prefixed schema types to SchemaTypeConfigProtos that were part of the new schema. - final Map mRewrittenPrefixedTypes = new ArrayMap<>(); - } - - /** - * Rewrites all types mentioned in the given {@code newSchema} to prepend {@code prefix}. - * Rewritten types will be added to the {@code existingSchema}. - * - * @param prefix The full prefix to prepend to the schema. - * @param existingSchema A schema that may contain existing types from across all prefixes. Will - * be mutated to contain the properly rewritten schema types from {@code newSchema}. - * @param newSchema Schema with types to add to the {@code existingSchema}. - * @return a RewrittenSchemaResults that contains all prefixed schema type names in the given - * prefix as well as a set of schema types that were deleted. - */ - @VisibleForTesting - static RewrittenSchemaResults rewriteSchema( - @NonNull String prefix, - @NonNull SchemaProto.Builder existingSchema, - @NonNull SchemaProto newSchema) - throws AppSearchException { - HashMap newTypesToProto = new HashMap<>(); - // Rewrite the schema type to include the typePrefix. - for (int typeIdx = 0; typeIdx < newSchema.getTypesCount(); typeIdx++) { - SchemaTypeConfigProto.Builder typeConfigBuilder = - newSchema.getTypes(typeIdx).toBuilder(); - - // Rewrite SchemaProto.types.schema_type - String newSchemaType = prefix + typeConfigBuilder.getSchemaType(); - typeConfigBuilder.setSchemaType(newSchemaType); - - // Rewrite SchemaProto.types.properties.schema_type - for (int propertyIdx = 0; - propertyIdx < typeConfigBuilder.getPropertiesCount(); - propertyIdx++) { - PropertyConfigProto.Builder propertyConfigBuilder = - typeConfigBuilder.getProperties(propertyIdx).toBuilder(); - if (!propertyConfigBuilder.getSchemaType().isEmpty()) { - String newPropertySchemaType = prefix + propertyConfigBuilder.getSchemaType(); - propertyConfigBuilder.setSchemaType(newPropertySchemaType); - typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder); - } - } - - newTypesToProto.put(newSchemaType, typeConfigBuilder.build()); - } - - // newTypesToProto is modified below, so we need a copy first - RewrittenSchemaResults rewrittenSchemaResults = new RewrittenSchemaResults(); - rewrittenSchemaResults.mRewrittenPrefixedTypes.putAll(newTypesToProto); - - // Combine the existing schema (which may have types from other prefixes) with this - // prefix's new schema. Modifies the existingSchemaBuilder. - // Check if we need to replace any old schema types with the new ones. - for (int i = 0; i < existingSchema.getTypesCount(); i++) { - String schemaType = existingSchema.getTypes(i).getSchemaType(); - SchemaTypeConfigProto newProto = newTypesToProto.remove(schemaType); - if (newProto != null) { - // Replacement - existingSchema.setTypes(i, newProto); - } else if (prefix.equals(getPrefix(schemaType))) { - // All types existing before but not in newSchema should be removed. - existingSchema.removeTypes(i); - --i; - rewrittenSchemaResults.mDeletedPrefixedTypes.add(schemaType); - } - } - // We've been removing existing types from newTypesToProto, so everything that remains is - // new. - existingSchema.addAllTypes(newTypesToProto.values()); - - return rewrittenSchemaResults; - } - - /** - * Rewrites the search spec filters with {@code prefixes}. - * - *

This method should be only called in query methods and get the READ lock to keep thread - * safety. - * - * @param searchSpecBuilder Client-provided SearchSpec - * @param prefixes Prefixes that we should prepend to all our filters - * @param allowedPrefixedSchemas Prefixed schemas that the client is allowed to query over. This - * supersedes the schema filters that may exist on the {@code searchSpecBuilder}. - * @return false if none there would be nothing to search over. - */ - @VisibleForTesting - @GuardedBy("mReadWriteLock") - boolean rewriteSearchSpecForPrefixesLocked( - @NonNull SearchSpecProto.Builder searchSpecBuilder, - @NonNull Set prefixes, - @NonNull Set allowedPrefixedSchemas) { - // Create a copy since retainAll() modifies the original set. - Set existingPrefixes = new ArraySet<>(mNamespaceMapLocked.keySet()); - existingPrefixes.retainAll(prefixes); - - if (existingPrefixes.isEmpty()) { - // None of the prefixes exist, empty query. - return false; - } - - if (allowedPrefixedSchemas.isEmpty()) { - // Not allowed to search over any schemas, empty query. - return false; - } - - // Clear the schema type filters since we'll be rewriting them with the - // allowedPrefixedSchemas. - searchSpecBuilder.clearSchemaTypeFilters(); - searchSpecBuilder.addAllSchemaTypeFilters(allowedPrefixedSchemas); - - // Cache the namespaces before clearing everything. - List namespaceFilters = searchSpecBuilder.getNamespaceFiltersList(); - searchSpecBuilder.clearNamespaceFilters(); - - // Rewrite non-schema filters to include a prefix. - for (String prefix : existingPrefixes) { - // TODO(b/169883602): We currently grab every namespace for every prefix. We can - // optimize this by checking if a prefix has any allowedSchemaTypes. If not, that - // means we don't want to query over anything in that prefix anyways, so we don't - // need to grab its namespaces either. - - // Empty namespaces on the search spec means to query over all namespaces. - Set existingNamespaces = mNamespaceMapLocked.get(prefix); - if (existingNamespaces != null) { - if (namespaceFilters.isEmpty()) { - // Include all namespaces - searchSpecBuilder.addAllNamespaceFilters(existingNamespaces); - } else { - // Prefix the given namespaces. - for (int i = 0; i < namespaceFilters.size(); i++) { - String prefixedNamespace = prefix + namespaceFilters.get(i); - if (existingNamespaces.contains(prefixedNamespace)) { - searchSpecBuilder.addNamespaceFilters(prefixedNamespace); - } - } - } - } - } - - return true; - } - - /** - * Returns the set of allowed prefixed schemas that the {@code prefix} can query while taking - * into account the {@code searchSpec} schema filters. - * - *

This only checks intersection of schema filters on the search spec with those that the - * prefix owns itself. This does not check global query permissions. - */ - @GuardedBy("mReadWriteLock") - private Set getAllowedPrefixSchemasLocked( - @NonNull String prefix, @NonNull SearchSpec searchSpec) { - Set allowedPrefixedSchemas = new ArraySet<>(); - - // Add all the schema filters the client specified. - List schemaFilters = searchSpec.getFilterSchemas(); - for (int i = 0; i < schemaFilters.size(); i++) { - allowedPrefixedSchemas.add(prefix + schemaFilters.get(i)); - } - - if (allowedPrefixedSchemas.isEmpty()) { - // If the client didn't specify any schema filters, search over all of their schemas - Map prefixedSchemaMap = mSchemaMapLocked.get(prefix); - if (prefixedSchemaMap != null) { - allowedPrefixedSchemas.addAll(prefixedSchemaMap.keySet()); - } - } - return allowedPrefixedSchemas; - } - - /** - * Rewrites the typePropertyMasks that exist in {@code prefixes}. - * - *

This method should be only called in query methods and get the READ lock to keep thread - * safety. - * - * @param resultSpecBuilder ResultSpecs as specified by client - * @param prefixes Prefixes that we should prepend to all our filters - * @param allowedPrefixedSchemas Prefixed schemas that the client is allowed to query over. - */ - @VisibleForTesting - @GuardedBy("mReadWriteLock") - void rewriteResultSpecForPrefixesLocked( - @NonNull ResultSpecProto.Builder resultSpecBuilder, - @NonNull Set prefixes, - @NonNull Set allowedPrefixedSchemas) { - // Create a copy since retainAll() modifies the original set. - Set existingPrefixes = new ArraySet<>(mNamespaceMapLocked.keySet()); - existingPrefixes.retainAll(prefixes); - - List prefixedTypePropertyMasks = new ArrayList<>(); - // Rewrite filters to include a database prefix. - for (String prefix : existingPrefixes) { - // Qualify the given schema types - for (TypePropertyMask typePropertyMask : resultSpecBuilder.getTypePropertyMasksList()) { - String unprefixedType = typePropertyMask.getSchemaType(); - boolean isWildcard = - unprefixedType.equals(SearchSpec.PROJECTION_SCHEMA_TYPE_WILDCARD); - String prefixedType = isWildcard ? unprefixedType : prefix + unprefixedType; - if (isWildcard || allowedPrefixedSchemas.contains(prefixedType)) { - prefixedTypePropertyMasks.add( - typePropertyMask.toBuilder().setSchemaType(prefixedType).build()); - } - } - } - resultSpecBuilder - .clearTypePropertyMasks() - .addAllTypePropertyMasks(prefixedTypePropertyMasks); - } - - /** - * Adds result groupings for each namespace in each package being queried for. - * - *

This method should be only called in query methods and get the READ lock to keep thread - * safety. - * - * @param resultSpecBuilder ResultSpecs as specified by client - * @param prefixes Prefixes that we should prepend to all our filters - * @param maxNumResults The maximum number of results for each grouping to support. - */ - @GuardedBy("mReadWriteLock") - private void addPerPackagePerNamespaceResultGroupingsLocked( - @NonNull ResultSpecProto.Builder resultSpecBuilder, - @NonNull Set prefixes, - int maxNumResults) { - Set existingPrefixes = new ArraySet<>(mNamespaceMapLocked.keySet()); - existingPrefixes.retainAll(prefixes); - - // Create a map for package+namespace to prefixedNamespaces. This is NOT necessarily the - // same as the list of namespaces. If one package has multiple databases, each with the same - // namespace, then those should be grouped together. - Map> packageAndNamespaceToNamespaces = new ArrayMap<>(); - for (String prefix : existingPrefixes) { - Set prefixedNamespaces = mNamespaceMapLocked.get(prefix); - if (prefixedNamespaces == null) { - continue; - } - String packageName = getPackageName(prefix); - // 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. - String emptyDatabasePrefix = createPrefix(packageName, /*databaseName*/ ""); - for (String prefixedNamespace : prefixedNamespaces) { - String namespace; - try { - namespace = removePrefix(prefixedNamespace); - } catch (AppSearchException e) { - // This should never happen. Skip this namespace if it does. - Log.e(TAG, "Prefixed namespace " + prefixedNamespace + " is malformed."); - continue; - } - String emptyDatabasePrefixedNamespace = emptyDatabasePrefix + namespace; - List namespaceList = - packageAndNamespaceToNamespaces.get(emptyDatabasePrefixedNamespace); - if (namespaceList == null) { - namespaceList = new ArrayList<>(); - packageAndNamespaceToNamespaces.put( - emptyDatabasePrefixedNamespace, namespaceList); - } - namespaceList.add(prefixedNamespace); - } - } - - for (List namespaces : packageAndNamespaceToNamespaces.values()) { - resultSpecBuilder.addResultGroupings( - ResultSpecProto.ResultGrouping.newBuilder() - .addAllNamespaces(namespaces) - .setMaxResults(maxNumResults)); - } - } - - /** - * Adds result groupings for each package being queried for. - * - *

This method should be only called in query methods and get the READ lock to keep thread - * safety. - * - * @param resultSpecBuilder ResultSpecs as specified by client - * @param prefixes Prefixes that we should prepend to all our filters - * @param maxNumResults The maximum number of results for each grouping to support. - */ - @GuardedBy("mReadWriteLock") - private void addPerPackageResultGroupingsLocked( - @NonNull ResultSpecProto.Builder resultSpecBuilder, - @NonNull Set prefixes, - int maxNumResults) { - Set existingPrefixes = new ArraySet<>(mNamespaceMapLocked.keySet()); - existingPrefixes.retainAll(prefixes); - - // Build up a map of package to namespaces. - Map> packageToNamespacesMap = new ArrayMap<>(); - for (String prefix : existingPrefixes) { - Set prefixedNamespaces = mNamespaceMapLocked.get(prefix); - if (prefixedNamespaces == null) { - continue; - } - String packageName = getPackageName(prefix); - List packageNamespaceList = packageToNamespacesMap.get(packageName); - if (packageNamespaceList == null) { - packageNamespaceList = new ArrayList<>(); - packageToNamespacesMap.put(packageName, packageNamespaceList); - } - packageNamespaceList.addAll(prefixedNamespaces); - } - - for (List prefixedNamespaces : packageToNamespacesMap.values()) { - resultSpecBuilder.addResultGroupings( - ResultSpecProto.ResultGrouping.newBuilder() - .addAllNamespaces(prefixedNamespaces) - .setMaxResults(maxNumResults)); - } - } - - /** - * Adds result groupings for each namespace being queried for. - * - *

This method should be only called in query methods and get the READ lock to keep thread - * safety. - * - * @param resultSpecBuilder ResultSpecs as specified by client - * @param prefixes Prefixes that we should prepend to all our filters - * @param maxNumResults The maximum number of results for each grouping to support. - */ - @GuardedBy("mReadWriteLock") - private void addPerNamespaceResultGroupingsLocked( - @NonNull ResultSpecProto.Builder resultSpecBuilder, - @NonNull Set prefixes, - int maxNumResults) { - Set existingPrefixes = new ArraySet<>(mNamespaceMapLocked.keySet()); - existingPrefixes.retainAll(prefixes); - - // Create a map of namespace to prefixedNamespaces. This is NOT necessarily the - // same as the list of namespaces. If a namespace exists under different packages and/or - // different databases, they should still be grouped together. - Map> namespaceToPrefixedNamespaces = new ArrayMap<>(); - for (String prefix : existingPrefixes) { - Set prefixedNamespaces = mNamespaceMapLocked.get(prefix); - if (prefixedNamespaces == null) { - continue; - } - for (String prefixedNamespace : prefixedNamespaces) { - String namespace; - try { - namespace = removePrefix(prefixedNamespace); - } catch (AppSearchException e) { - // This should never happen. Skip this namespace if it does. - Log.e(TAG, "Prefixed namespace " + prefixedNamespace + " is malformed."); - continue; - } - List groupedPrefixedNamespaces = - namespaceToPrefixedNamespaces.get(namespace); - if (groupedPrefixedNamespaces == null) { - groupedPrefixedNamespaces = new ArrayList<>(); - namespaceToPrefixedNamespaces.put(namespace, groupedPrefixedNamespaces); - } - groupedPrefixedNamespaces.add(prefixedNamespace); - } - } - - for (List namespaces : namespaceToPrefixedNamespaces.values()) { - resultSpecBuilder.addResultGroupings( - ResultSpecProto.ResultGrouping.newBuilder() - .addAllNamespaces(namespaces) - .setMaxResults(maxNumResults)); - } - } - - @VisibleForTesting - @GuardedBy("mReadWriteLock") - SchemaProto getSchemaProtoLocked() throws AppSearchException { - mLogUtil.piiTrace("getSchema, request"); - 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) only allow GetSchemaResultProto NOT_FOUND on first run - checkCodeOneOf(schemaProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND); - return schemaProto.getSchema(); - } - - private void addNextPageToken(String packageName, long nextPageToken) { - if (nextPageToken == EMPTY_PAGE_TOKEN) { - // There is no more pages. No need to add it. - return; - } - synchronized (mNextPageTokensLocked) { - Set tokens = mNextPageTokensLocked.get(packageName); - if (tokens == null) { - tokens = new ArraySet<>(); - mNextPageTokensLocked.put(packageName, tokens); - } - tokens.add(nextPageToken); - } - } - - private void checkNextPageToken(String packageName, long nextPageToken) - throws AppSearchException { - if (nextPageToken == EMPTY_PAGE_TOKEN) { - // Swallow the check for empty page token, token = 0 means there is no more page and it - // won't return anything from Icing. - return; - } - synchronized (mNextPageTokensLocked) { - Set nextPageTokens = mNextPageTokensLocked.get(packageName); - if (nextPageTokens == null || !nextPageTokens.contains(nextPageToken)) { - throw new AppSearchException( - AppSearchResult.RESULT_SECURITY_ERROR, - "Package \"" - + packageName - + "\" cannot use nextPageToken: " - + nextPageToken); - } - } - } - - private static void addToMap( - Map> map, String prefix, String prefixedValue) { - Set values = map.get(prefix); - if (values == null) { - values = new ArraySet<>(); - map.put(prefix, values); - } - values.add(prefixedValue); - } - - private static void addToMap( - Map> map, - String prefix, - SchemaTypeConfigProto schemaTypeConfigProto) { - Map schemaTypeMap = map.get(prefix); - if (schemaTypeMap == null) { - schemaTypeMap = new ArrayMap<>(); - map.put(prefix, schemaTypeMap); - } - schemaTypeMap.put(schemaTypeConfigProto.getSchemaType(), schemaTypeConfigProto); - } - - private static void removeFromMap( - Map> map, String prefix, String schemaType) { - Map schemaTypeMap = map.get(prefix); - if (schemaTypeMap != null) { - schemaTypeMap.remove(schemaType); - } - } - - /** - * Checks the given status code and throws an {@link AppSearchException} if code is an error. - * - * @throws AppSearchException on error codes. - */ - private static void checkSuccess(StatusProto statusProto) throws AppSearchException { - checkCodeOneOf(statusProto, StatusProto.Code.OK); - } - - /** - * Checks the given status code is one of the provided codes, and throws an {@link - * AppSearchException} if it is not. - */ - private static void checkCodeOneOf(StatusProto statusProto, StatusProto.Code... codes) - throws AppSearchException { - for (int i = 0; i < codes.length; i++) { - if (codes[i] == statusProto.getCode()) { - // Everything's good - return; - } - } - - if (statusProto.getCode() == StatusProto.Code.WARNING_DATA_LOSS) { - // TODO: May want to propagate WARNING_DATA_LOSS up to AppSearchSession so they can - // choose to log the error or potentially pass it on to clients. - Log.w(TAG, "Encountered WARNING_DATA_LOSS: " + statusProto.getMessage()); - return; - } - - throw new AppSearchException( - ResultCodeToProtoConverter.toResultCode(statusProto.getCode()), - statusProto.getMessage()); - } - - /** - * Checks whether {@link IcingSearchEngine#optimize()} should be called to release resources. - * - *

This method should be only called after a mutation to local storage backend which deletes - * a mass of data and could release lots resources after {@link IcingSearchEngine#optimize()}. - * - *

This method will trigger {@link IcingSearchEngine#getOptimizeInfo()} to check resources - * that could be released for every {@link #CHECK_OPTIMIZE_INTERVAL} mutations. - * - *

{@link IcingSearchEngine#optimize()} should be called only if {@link - * GetOptimizeInfoResultProto} shows there is enough resources could be released. - * - * @param mutationSize The number of how many mutations have been executed for current request. - * An inside counter will accumulates it. Once the counter reaches {@link - * #CHECK_OPTIMIZE_INTERVAL}, {@link IcingSearchEngine#getOptimizeInfo()} will be triggered - * and the counter will be reset. - */ - public void checkForOptimize(int mutationSize, @Nullable OptimizeStats.Builder builder) - throws AppSearchException { - mReadWriteLock.writeLock().lock(); - try { - mOptimizeIntervalCountLocked += mutationSize; - if (mOptimizeIntervalCountLocked >= CHECK_OPTIMIZE_INTERVAL) { - checkForOptimize(builder); - } - } finally { - mReadWriteLock.writeLock().unlock(); - } - } - - /** - * Checks whether {@link IcingSearchEngine#optimize()} should be called to release resources. - * - *

This method will directly trigger {@link IcingSearchEngine#getOptimizeInfo()} to check - * resources that could be released. - * - *

{@link IcingSearchEngine#optimize()} should be called only if {@link - * OptimizeStrategy#shouldOptimize(GetOptimizeInfoResultProto)} return true. - */ - public void checkForOptimize(@Nullable OptimizeStats.Builder builder) - throws AppSearchException { - mReadWriteLock.writeLock().lock(); - try { - GetOptimizeInfoResultProto optimizeInfo = getOptimizeInfoResultLocked(); - checkSuccess(optimizeInfo.getStatus()); - mOptimizeIntervalCountLocked = 0; - if (mOptimizeStrategy.shouldOptimize(optimizeInfo)) { - optimize(builder); - } - } finally { - mReadWriteLock.writeLock().unlock(); - } - // TODO(b/147699081): Return OptimizeResultProto & log lost data detail once we add - // a field to indicate lost_schema and lost_documents in OptimizeResultProto. - // go/icing-library-apis. - } - - /** Triggers {@link IcingSearchEngine#optimize()} directly. */ - public void optimize(@Nullable OptimizeStats.Builder builder) throws AppSearchException { - mReadWriteLock.writeLock().lock(); - try { - mLogUtil.piiTrace("optimize, request"); - OptimizeResultProto optimizeResultProto = mIcingSearchEngineLocked.optimize(); - mLogUtil.piiTrace( - "optimize, response", optimizeResultProto.getStatus(), optimizeResultProto); - if (builder != null) { - builder.setStatusCode(statusProtoToResultCode(optimizeResultProto.getStatus())); - AppSearchLoggerHelper.copyNativeStats( - optimizeResultProto.getOptimizeStats(), builder); - } - checkSuccess(optimizeResultProto.getStatus()); - } finally { - mReadWriteLock.writeLock().unlock(); - } - } - - /** Remove the rewritten schema types from any result documents. */ - @NonNull - @VisibleForTesting - static SearchResultPage rewriteSearchResultProto( - @NonNull SearchResultProto searchResultProto, - @NonNull Map> schemaMap) - throws AppSearchException { - // Parallel array of package names for each document search result. - List packageNames = new ArrayList<>(searchResultProto.getResultsCount()); - - // Parallel array of database names for each document search result. - List databaseNames = new ArrayList<>(searchResultProto.getResultsCount()); - - SearchResultProto.Builder resultsBuilder = searchResultProto.toBuilder(); - for (int i = 0; i < searchResultProto.getResultsCount(); i++) { - SearchResultProto.ResultProto.Builder resultBuilder = - searchResultProto.getResults(i).toBuilder(); - DocumentProto.Builder documentBuilder = resultBuilder.getDocument().toBuilder(); - String prefix = removePrefixesFromDocument(documentBuilder); - packageNames.add(getPackageName(prefix)); - databaseNames.add(getDatabaseName(prefix)); - resultBuilder.setDocument(documentBuilder); - resultsBuilder.setResults(i, resultBuilder); - } - return SearchResultToProtoConverter.toSearchResultPage( - resultsBuilder, packageNames, databaseNames, schemaMap); - } - - @GuardedBy("mReadWriteLock") - @VisibleForTesting - GetOptimizeInfoResultProto getOptimizeInfoResultLocked() { - mLogUtil.piiTrace("getOptimizeInfo, request"); - GetOptimizeInfoResultProto result = mIcingSearchEngineLocked.getOptimizeInfo(); - mLogUtil.piiTrace("getOptimizeInfo, response", result.getStatus(), result); - return result; - } - - /** - * Converts an erroneous status code from the Icing status enums to the AppSearchResult enums. - * - *

Callers should ensure that the status code is not OK or WARNING_DATA_LOSS. - * - * @param statusProto StatusProto with error code to translate into an {@link AppSearchResult} - * code. - * @return {@link AppSearchResult} error code - */ - private static @AppSearchResult.ResultCode int statusProtoToResultCode( - @NonNull StatusProto statusProto) { - return ResultCodeToProtoConverter.toResultCode(statusProto.getCode()); - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java deleted file mode 100644 index 98cedc7e6b544..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java +++ /dev/null @@ -1,58 +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 com.android.server.appsearch.external.localstorage; - -import android.annotation.NonNull; - -import com.android.server.appsearch.external.localstorage.stats.CallStats; -import com.android.server.appsearch.external.localstorage.stats.InitializeStats; -import com.android.server.appsearch.external.localstorage.stats.OptimizeStats; -import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; -import com.android.server.appsearch.external.localstorage.stats.RemoveStats; -import com.android.server.appsearch.external.localstorage.stats.SearchStats; - -/** - * An interface for implementing client-defined logging AppSearch operations stats. - * - *

Any implementation needs to provide general information on how to log all the stats types. - * (e.g. {@link CallStats}) - * - *

All implementations of this interface must be thread safe. - * - * @hide - */ -public interface AppSearchLogger { - /** Logs {@link CallStats} */ - void logStats(@NonNull CallStats stats); - - /** Logs {@link PutDocumentStats} */ - void logStats(@NonNull PutDocumentStats stats); - - /** Logs {@link InitializeStats} */ - void logStats(@NonNull InitializeStats stats); - - /** Logs {@link SearchStats} */ - void logStats(@NonNull SearchStats stats); - - /** Logs {@link RemoveStats} */ - void logStats(@NonNull RemoveStats stats); - - /** Logs {@link OptimizeStats} */ - void logStats(@NonNull OptimizeStats stats); - - // TODO(b/173532925) Add remaining logStats once we add all the stats. -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java deleted file mode 100644 index cd653e569f118..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage; - -import android.annotation.NonNull; - -import com.android.server.appsearch.external.localstorage.stats.InitializeStats; -import com.android.server.appsearch.external.localstorage.stats.OptimizeStats; -import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; -import com.android.server.appsearch.external.localstorage.stats.RemoveStats; -import com.android.server.appsearch.external.localstorage.stats.SearchStats; - -import com.google.android.icing.proto.DeleteStatsProto; -import com.google.android.icing.proto.InitializeStatsProto; -import com.google.android.icing.proto.OptimizeStatsProto; -import com.google.android.icing.proto.PutDocumentStatsProto; -import com.google.android.icing.proto.QueryStatsProto; - -import java.util.Objects; - -/** - * Class contains helper functions for logging. - * - *

E.g. we need to have helper functions to copy numbers from IcingLib to stats classes. - * - * @hide - */ -public final class AppSearchLoggerHelper { - private AppSearchLoggerHelper() {} - - /** - * Copies native PutDocument stats to builder. - * - * @param fromNativeStats stats copied from - * @param toStatsBuilder stats copied to - */ - static void copyNativeStats( - @NonNull PutDocumentStatsProto fromNativeStats, - @NonNull PutDocumentStats.Builder toStatsBuilder) { - Objects.requireNonNull(fromNativeStats); - Objects.requireNonNull(toStatsBuilder); - toStatsBuilder - .setNativeLatencyMillis(fromNativeStats.getLatencyMs()) - .setNativeDocumentStoreLatencyMillis(fromNativeStats.getDocumentStoreLatencyMs()) - .setNativeIndexLatencyMillis(fromNativeStats.getIndexLatencyMs()) - .setNativeIndexMergeLatencyMillis(fromNativeStats.getIndexMergeLatencyMs()) - .setNativeDocumentSizeBytes(fromNativeStats.getDocumentSize()) - .setNativeNumTokensIndexed( - fromNativeStats.getTokenizationStats().getNumTokensIndexed()) - .setNativeExceededMaxNumTokens( - fromNativeStats.getTokenizationStats().getExceededMaxTokenNum()); - } - - /** - * Copies native Initialize stats to builder. - * - * @param fromNativeStats stats copied from - * @param toStatsBuilder stats copied to - */ - static void copyNativeStats( - @NonNull InitializeStatsProto fromNativeStats, - @NonNull InitializeStats.Builder toStatsBuilder) { - Objects.requireNonNull(fromNativeStats); - Objects.requireNonNull(toStatsBuilder); - toStatsBuilder - .setNativeLatencyMillis(fromNativeStats.getLatencyMs()) - .setDocumentStoreRecoveryCause( - fromNativeStats.getDocumentStoreRecoveryCause().getNumber()) - .setIndexRestorationCause(fromNativeStats.getIndexRestorationCause().getNumber()) - .setSchemaStoreRecoveryCause( - fromNativeStats.getSchemaStoreRecoveryCause().getNumber()) - .setDocumentStoreRecoveryLatencyMillis( - fromNativeStats.getDocumentStoreRecoveryLatencyMs()) - .setIndexRestorationLatencyMillis(fromNativeStats.getIndexRestorationLatencyMs()) - .setSchemaStoreRecoveryLatencyMillis( - fromNativeStats.getSchemaStoreRecoveryLatencyMs()) - .setDocumentStoreDataStatus( - fromNativeStats.getDocumentStoreDataStatus().getNumber()) - .setDocumentCount(fromNativeStats.getNumDocuments()) - .setSchemaTypeCount(fromNativeStats.getNumSchemaTypes()); - } - - /** - * Copies native Query stats to builder. - * - * @param fromNativeStats Stats copied from. - * @param toStatsBuilder Stats copied to. - */ - static void copyNativeStats( - @NonNull QueryStatsProto fromNativeStats, @NonNull SearchStats.Builder toStatsBuilder) { - Objects.requireNonNull(fromNativeStats); - Objects.requireNonNull(toStatsBuilder); - toStatsBuilder - .setNativeLatencyMillis(fromNativeStats.getLatencyMs()) - .setTermCount(fromNativeStats.getNumTerms()) - .setQueryLength(fromNativeStats.getQueryLength()) - .setFilteredNamespaceCount(fromNativeStats.getNumNamespacesFiltered()) - .setFilteredSchemaTypeCount(fromNativeStats.getNumSchemaTypesFiltered()) - .setRequestedPageSize(fromNativeStats.getRequestedPageSize()) - .setCurrentPageReturnedResultCount( - fromNativeStats.getNumResultsReturnedCurrentPage()) - .setIsFirstPage(fromNativeStats.getIsFirstPage()) - .setParseQueryLatencyMillis(fromNativeStats.getParseQueryLatencyMs()) - .setRankingStrategy(fromNativeStats.getRankingStrategy().getNumber()) - .setScoredDocumentCount(fromNativeStats.getNumDocumentsScored()) - .setScoringLatencyMillis(fromNativeStats.getScoringLatencyMs()) - .setRankingLatencyMillis(fromNativeStats.getRankingLatencyMs()) - .setResultWithSnippetsCount(fromNativeStats.getNumResultsWithSnippets()) - .setDocumentRetrievingLatencyMillis( - fromNativeStats.getDocumentRetrievalLatencyMs()); - } - - /** - * Copies native Delete stats to builder. - * - * @param fromNativeStats Stats copied from. - * @param toStatsBuilder Stats copied to. - */ - static void copyNativeStats( - @NonNull DeleteStatsProto fromNativeStats, - @NonNull RemoveStats.Builder toStatsBuilder) { - Objects.requireNonNull(fromNativeStats); - Objects.requireNonNull(toStatsBuilder); - toStatsBuilder - .setNativeLatencyMillis(fromNativeStats.getLatencyMs()) - .setDeleteType(fromNativeStats.getDeleteType().getNumber()) - .setDeletedDocumentCount(fromNativeStats.getNumDocumentsDeleted()); - } - - /** - * Copies native {@link OptimizeStatsProto} to builder. - * - * @param fromNativeStats Stats copied from. - * @param toStatsBuilder Stats copied to. - */ - static void copyNativeStats( - @NonNull OptimizeStatsProto fromNativeStats, - @NonNull OptimizeStats.Builder toStatsBuilder) { - Objects.requireNonNull(fromNativeStats); - Objects.requireNonNull(toStatsBuilder); - toStatsBuilder - .setNativeLatencyMillis(fromNativeStats.getLatencyMs()) - .setDocumentStoreOptimizeLatencyMillis( - fromNativeStats.getDocumentStoreOptimizeLatencyMs()) - .setIndexRestorationLatencyMillis(fromNativeStats.getIndexRestorationLatencyMs()) - .setOriginalDocumentCount(fromNativeStats.getNumOriginalDocuments()) - .setDeletedDocumentCount(fromNativeStats.getNumDeletedDocuments()) - .setExpiredDocumentCount(fromNativeStats.getNumExpiredDocuments()) - .setStorageSizeBeforeBytes(fromNativeStats.getStorageSizeBefore()) - .setStorageSizeAfterBytes(fromNativeStats.getStorageSizeAfter()) - .setTimeSinceLastOptimizeMillis(fromNativeStats.getTimeSinceLastOptimizeMs()); - } -} 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 deleted file mode 100644 index 3f5723ee53e07..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/LimitConfig.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage; - - -/** - * 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. - * - *

Enforced at the time of serializing the document into a proto. - * - *

This limit has two purposes: - * - *

    - *
  1. 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 - *
  2. Prevent apps from using a very large amount of data by storing exceptionally large - * documents. - *
- */ - int getMaxDocumentSizeBytes(); - - /** - * The maximum number of documents a single app is allowed to index. - * - *

Enforced at indexing time. - * - *

This limit has two purposes: - * - *

    - *
  1. Protect icing lib's docid space from being overwhelmed by a single app. The overall - * docid limit is currently 2^20 (~1 million) - *
  2. Prevent apps from using a very large amount of data on the system by storing too many - * documents. - *
- */ - int getMaxDocumentCount(); -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/OptimizeStrategy.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/OptimizeStrategy.java deleted file mode 100644 index 6cb84bc64eb9d..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/OptimizeStrategy.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage; - -import android.annotation.NonNull; - -import com.google.android.icing.proto.GetOptimizeInfoResultProto; - -/** - * An interface class for implementing a strategy to determine when to trigger {@link - * AppSearchImpl#optimize()}. - * - * @hide - */ -public interface OptimizeStrategy { - - /** - * Determines whether {@link AppSearchImpl#optimize()} need to be triggered to release garbage - * resources in AppSearch base on the given information. - * - * @param optimizeInfo The proto object indicates the number of garbage resources in AppSearch. - * @return {@code true} if {@link AppSearchImpl#optimize()} need to be triggered. - */ - boolean shouldOptimize(@NonNull GetOptimizeInfoResultProto optimizeInfo); -} 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 deleted file mode 100644 index 0fabab04048ba..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/UnlimitedLimitConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage; - - -/** - * 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/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java deleted file mode 100644 index 9ce916fa60d80..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverter.java +++ /dev/null @@ -1,221 +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 com.android.server.appsearch.external.localstorage.converter; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchSchema; -import android.app.appsearch.GenericDocument; - -import com.google.android.icing.proto.DocumentProto; -import com.google.android.icing.proto.PropertyProto; -import com.google.android.icing.proto.SchemaTypeConfigProto; -import com.google.protobuf.ByteString; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Map; -import java.util.Objects; - -/** - * Translates a {@link GenericDocument} into a {@link DocumentProto}. - * - * @hide - */ -public final class GenericDocumentToProtoConverter { - private static final String[] EMPTY_STRING_ARRAY = new String[0]; - private static final long[] EMPTY_LONG_ARRAY = new long[0]; - private static final double[] EMPTY_DOUBLE_ARRAY = new double[0]; - private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0]; - private static final byte[][] EMPTY_BYTES_ARRAY = new byte[0][0]; - private static final GenericDocument[] EMPTY_DOCUMENT_ARRAY = new GenericDocument[0]; - - private GenericDocumentToProtoConverter() {} - - /** Converts a {@link GenericDocument} into a {@link DocumentProto}. */ - @NonNull - @SuppressWarnings("unchecked") - public static DocumentProto toDocumentProto(@NonNull GenericDocument document) { - Objects.requireNonNull(document); - DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder(); - mProtoBuilder - .setUri(document.getId()) - .setSchema(document.getSchemaType()) - .setNamespace(document.getNamespace()) - .setScore(document.getScore()) - .setTtlMs(document.getTtlMillis()) - .setCreationTimestampMs(document.getCreationTimestampMillis()); - ArrayList keys = new ArrayList<>(document.getPropertyNames()); - Collections.sort(keys); - for (int i = 0; i < keys.size(); i++) { - String name = keys.get(i); - PropertyProto.Builder propertyProto = PropertyProto.newBuilder().setName(name); - Object property = document.getProperty(name); - if (property instanceof String[]) { - String[] stringValues = (String[]) property; - for (int j = 0; j < stringValues.length; j++) { - propertyProto.addStringValues(stringValues[j]); - } - } else if (property instanceof long[]) { - long[] longValues = (long[]) property; - for (int j = 0; j < longValues.length; j++) { - propertyProto.addInt64Values(longValues[j]); - } - } else if (property instanceof double[]) { - double[] doubleValues = (double[]) property; - for (int j = 0; j < doubleValues.length; j++) { - propertyProto.addDoubleValues(doubleValues[j]); - } - } else if (property instanceof boolean[]) { - boolean[] booleanValues = (boolean[]) property; - for (int j = 0; j < booleanValues.length; j++) { - propertyProto.addBooleanValues(booleanValues[j]); - } - } else if (property instanceof byte[][]) { - byte[][] bytesValues = (byte[][]) property; - for (int j = 0; j < bytesValues.length; j++) { - propertyProto.addBytesValues(ByteString.copyFrom(bytesValues[j])); - } - } else if (property instanceof GenericDocument[]) { - GenericDocument[] documentValues = (GenericDocument[]) property; - for (int j = 0; j < documentValues.length; j++) { - DocumentProto proto = toDocumentProto(documentValues[j]); - propertyProto.addDocumentValues(proto); - } - } else { - throw new IllegalStateException( - String.format( - "Property \"%s\" has unsupported value type %s", - name, property.getClass().toString())); - } - mProtoBuilder.addProperties(propertyProto); - } - return mProtoBuilder.build(); - } - - /** - * Converts a {@link DocumentProto} into a {@link GenericDocument}. - * - *

In the case that the {@link DocumentProto} object proto has no values set, the converter - * searches for the matching property name in the {@link SchemaTypeConfigProto} object for the - * document, and infers the correct default value to set for the empty property based on the - * data type of the property defined by the schema type. - * - * @param proto the document to convert to a {@link GenericDocument} instance. The document - * proto should have its package + database prefix stripped from its fields. - * @param prefix the package + database prefix used searching the {@code schemaTypeMap}. - * @param schemaTypeMap map of prefixed schema type to {@link SchemaTypeConfigProto}, used for - * looking up the default empty value to set for a document property that has all empty - * values. - */ - @NonNull - public static GenericDocument toGenericDocument( - @NonNull DocumentProto proto, - @NonNull String prefix, - @NonNull Map schemaTypeMap) { - Objects.requireNonNull(proto); - GenericDocument.Builder documentBuilder = - new GenericDocument.Builder<>( - proto.getNamespace(), proto.getUri(), proto.getSchema()) - .setScore(proto.getScore()) - .setTtlMillis(proto.getTtlMs()) - .setCreationTimestampMillis(proto.getCreationTimestampMs()); - String prefixedSchemaType = prefix + proto.getSchema(); - - for (int i = 0; i < proto.getPropertiesCount(); i++) { - PropertyProto property = proto.getProperties(i); - String name = property.getName(); - if (property.getStringValuesCount() > 0) { - String[] values = new String[property.getStringValuesCount()]; - for (int j = 0; j < values.length; j++) { - values[j] = property.getStringValues(j); - } - documentBuilder.setPropertyString(name, values); - } else if (property.getInt64ValuesCount() > 0) { - long[] values = new long[property.getInt64ValuesCount()]; - for (int j = 0; j < values.length; j++) { - values[j] = property.getInt64Values(j); - } - documentBuilder.setPropertyLong(name, values); - } else if (property.getDoubleValuesCount() > 0) { - double[] values = new double[property.getDoubleValuesCount()]; - for (int j = 0; j < values.length; j++) { - values[j] = property.getDoubleValues(j); - } - documentBuilder.setPropertyDouble(name, values); - } else if (property.getBooleanValuesCount() > 0) { - boolean[] values = new boolean[property.getBooleanValuesCount()]; - for (int j = 0; j < values.length; j++) { - values[j] = property.getBooleanValues(j); - } - documentBuilder.setPropertyBoolean(name, values); - } else if (property.getBytesValuesCount() > 0) { - byte[][] values = new byte[property.getBytesValuesCount()][]; - for (int j = 0; j < values.length; j++) { - values[j] = property.getBytesValues(j).toByteArray(); - } - documentBuilder.setPropertyBytes(name, values); - } else if (property.getDocumentValuesCount() > 0) { - GenericDocument[] values = new GenericDocument[property.getDocumentValuesCount()]; - for (int j = 0; j < values.length; j++) { - values[j] = - toGenericDocument(property.getDocumentValues(j), prefix, schemaTypeMap); - } - documentBuilder.setPropertyDocument(name, values); - } else { - // TODO(b/184966497): Optimize by caching PropertyConfigProto - setEmptyProperty(name, documentBuilder, schemaTypeMap.get(prefixedSchemaType)); - } - } - return documentBuilder.build(); - } - - private static void setEmptyProperty( - @NonNull String propertyName, - @NonNull GenericDocument.Builder documentBuilder, - @NonNull SchemaTypeConfigProto schema) { - @AppSearchSchema.PropertyConfig.DataType int dataType = 0; - for (int i = 0; i < schema.getPropertiesCount(); ++i) { - if (propertyName.equals(schema.getProperties(i).getPropertyName())) { - dataType = schema.getProperties(i).getDataType().getNumber(); - break; - } - } - - switch (dataType) { - case AppSearchSchema.PropertyConfig.DATA_TYPE_STRING: - documentBuilder.setPropertyString(propertyName, EMPTY_STRING_ARRAY); - break; - case AppSearchSchema.PropertyConfig.DATA_TYPE_LONG: - documentBuilder.setPropertyLong(propertyName, EMPTY_LONG_ARRAY); - break; - case AppSearchSchema.PropertyConfig.DATA_TYPE_DOUBLE: - documentBuilder.setPropertyDouble(propertyName, EMPTY_DOUBLE_ARRAY); - break; - case AppSearchSchema.PropertyConfig.DATA_TYPE_BOOLEAN: - documentBuilder.setPropertyBoolean(propertyName, EMPTY_BOOLEAN_ARRAY); - break; - case AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES: - documentBuilder.setPropertyBytes(propertyName, EMPTY_BYTES_ARRAY); - break; - case AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT: - documentBuilder.setPropertyDocument(propertyName, EMPTY_DOCUMENT_ARRAY); - break; - default: - throw new IllegalStateException("Unknown type of value: " + propertyName); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/ResultCodeToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/ResultCodeToProtoConverter.java deleted file mode 100644 index e340de0a5802c..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/ResultCodeToProtoConverter.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage.converter; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchResult; -import android.util.Log; - -import com.google.android.icing.proto.StatusProto; - -/** - * Translates an {@link StatusProto.Code} into a {@link AppSearchResult.ResultCode} - * - * @hide - */ -public final class ResultCodeToProtoConverter { - - private static final String TAG = "AppSearchResultCodeToPr"; - - private ResultCodeToProtoConverter() {} - - /** Converts an {@link StatusProto.Code} into a {@link AppSearchResult.ResultCode}. */ - public static @AppSearchResult.ResultCode int toResultCode( - @NonNull StatusProto.Code statusCode) { - switch (statusCode) { - case OK: - return AppSearchResult.RESULT_OK; - case OUT_OF_SPACE: - return AppSearchResult.RESULT_OUT_OF_SPACE; - case INTERNAL: - return AppSearchResult.RESULT_INTERNAL_ERROR; - case UNKNOWN: - return AppSearchResult.RESULT_UNKNOWN_ERROR; - case NOT_FOUND: - return AppSearchResult.RESULT_NOT_FOUND; - case INVALID_ARGUMENT: - return AppSearchResult.RESULT_INVALID_ARGUMENT; - default: - // Some unknown/unsupported error - Log.e( - TAG, - "Cannot convert IcingSearchEngine status code: " - + statusCode - + " to AppSearchResultCode."); - return AppSearchResult.RESULT_INTERNAL_ERROR; - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java deleted file mode 100644 index 3e4e7d2f9c2b1..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverter.java +++ /dev/null @@ -1,227 +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 com.android.server.appsearch.external.localstorage.converter; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchSchema; -import android.util.Log; - -import com.google.android.icing.proto.DocumentIndexingConfig; -import com.google.android.icing.proto.PropertyConfigProto; -import com.google.android.icing.proto.SchemaTypeConfigProto; -import com.google.android.icing.proto.SchemaTypeConfigProtoOrBuilder; -import com.google.android.icing.proto.StringIndexingConfig; -import com.google.android.icing.proto.TermMatchType; - -import java.util.List; -import java.util.Objects; - -/** - * Translates an {@link AppSearchSchema} into a {@link SchemaTypeConfigProto}. - * - * @hide - */ -public final class SchemaToProtoConverter { - private static final String TAG = "AppSearchSchemaToProtoC"; - - private SchemaToProtoConverter() {} - - /** - * Converts an {@link android.app.appsearch.AppSearchSchema} into a {@link - * SchemaTypeConfigProto}. - */ - @NonNull - public static SchemaTypeConfigProto toSchemaTypeConfigProto( - @NonNull AppSearchSchema schema, int version) { - Objects.requireNonNull(schema); - SchemaTypeConfigProto.Builder protoBuilder = - SchemaTypeConfigProto.newBuilder() - .setSchemaType(schema.getSchemaType()) - .setVersion(version); - List properties = schema.getProperties(); - for (int i = 0; i < properties.size(); i++) { - PropertyConfigProto propertyProto = toPropertyConfigProto(properties.get(i)); - protoBuilder.addProperties(propertyProto); - } - return protoBuilder.build(); - } - - @NonNull - private static PropertyConfigProto toPropertyConfigProto( - @NonNull AppSearchSchema.PropertyConfig property) { - Objects.requireNonNull(property); - PropertyConfigProto.Builder builder = - PropertyConfigProto.newBuilder().setPropertyName(property.getName()); - - // Set dataType - @AppSearchSchema.PropertyConfig.DataType int dataType = property.getDataType(); - PropertyConfigProto.DataType.Code dataTypeProto = - PropertyConfigProto.DataType.Code.forNumber(dataType); - if (dataTypeProto == null) { - throw new IllegalArgumentException("Invalid dataType: " + dataType); - } - builder.setDataType(dataTypeProto); - - // Set cardinality - @AppSearchSchema.PropertyConfig.Cardinality int cardinality = property.getCardinality(); - PropertyConfigProto.Cardinality.Code cardinalityProto = - PropertyConfigProto.Cardinality.Code.forNumber(cardinality); - if (cardinalityProto == null) { - throw new IllegalArgumentException("Invalid cardinality: " + dataType); - } - builder.setCardinality(cardinalityProto); - - if (property instanceof AppSearchSchema.StringPropertyConfig) { - AppSearchSchema.StringPropertyConfig stringProperty = - (AppSearchSchema.StringPropertyConfig) property; - StringIndexingConfig stringIndexingConfig = - StringIndexingConfig.newBuilder() - .setTermMatchType( - convertTermMatchTypeToProto(stringProperty.getIndexingType())) - .setTokenizerType( - convertTokenizerTypeToProto(stringProperty.getTokenizerType())) - .build(); - builder.setStringIndexingConfig(stringIndexingConfig); - - } else if (property instanceof AppSearchSchema.DocumentPropertyConfig) { - AppSearchSchema.DocumentPropertyConfig documentProperty = - (AppSearchSchema.DocumentPropertyConfig) property; - builder.setSchemaType(documentProperty.getSchemaType()) - .setDocumentIndexingConfig( - DocumentIndexingConfig.newBuilder() - .setIndexNestedProperties( - documentProperty.shouldIndexNestedProperties())); - } - return builder.build(); - } - - /** - * Converts a {@link SchemaTypeConfigProto} into an {@link - * android.app.appsearch.AppSearchSchema}. - */ - @NonNull - public static AppSearchSchema toAppSearchSchema(@NonNull SchemaTypeConfigProtoOrBuilder proto) { - Objects.requireNonNull(proto); - AppSearchSchema.Builder builder = new AppSearchSchema.Builder(proto.getSchemaType()); - List properties = proto.getPropertiesList(); - for (int i = 0; i < properties.size(); i++) { - AppSearchSchema.PropertyConfig propertyConfig = toPropertyConfig(properties.get(i)); - builder.addProperty(propertyConfig); - } - return builder.build(); - } - - @NonNull - private static AppSearchSchema.PropertyConfig toPropertyConfig( - @NonNull PropertyConfigProto proto) { - Objects.requireNonNull(proto); - switch (proto.getDataType()) { - case STRING: - return toStringPropertyConfig(proto); - case INT64: - return new AppSearchSchema.LongPropertyConfig.Builder(proto.getPropertyName()) - .setCardinality(proto.getCardinality().getNumber()) - .build(); - case DOUBLE: - return new AppSearchSchema.DoublePropertyConfig.Builder(proto.getPropertyName()) - .setCardinality(proto.getCardinality().getNumber()) - .build(); - case BOOLEAN: - return new AppSearchSchema.BooleanPropertyConfig.Builder(proto.getPropertyName()) - .setCardinality(proto.getCardinality().getNumber()) - .build(); - case BYTES: - return new AppSearchSchema.BytesPropertyConfig.Builder(proto.getPropertyName()) - .setCardinality(proto.getCardinality().getNumber()) - .build(); - case DOCUMENT: - return toDocumentPropertyConfig(proto); - default: - throw new IllegalArgumentException("Invalid dataType: " + proto.getDataType()); - } - } - - @NonNull - private static AppSearchSchema.StringPropertyConfig toStringPropertyConfig( - @NonNull PropertyConfigProto proto) { - AppSearchSchema.StringPropertyConfig.Builder builder = - new AppSearchSchema.StringPropertyConfig.Builder(proto.getPropertyName()) - .setCardinality(proto.getCardinality().getNumber()) - .setTokenizerType( - proto.getStringIndexingConfig().getTokenizerType().getNumber()); - - // Set indexingType - TermMatchType.Code termMatchTypeProto = proto.getStringIndexingConfig().getTermMatchType(); - builder.setIndexingType(convertTermMatchTypeFromProto(termMatchTypeProto)); - - return builder.build(); - } - - @NonNull - private static AppSearchSchema.DocumentPropertyConfig toDocumentPropertyConfig( - @NonNull PropertyConfigProto proto) { - return new AppSearchSchema.DocumentPropertyConfig.Builder( - proto.getPropertyName(), proto.getSchemaType()) - .setCardinality(proto.getCardinality().getNumber()) - .setShouldIndexNestedProperties( - proto.getDocumentIndexingConfig().getIndexNestedProperties()) - .build(); - } - - @NonNull - private static TermMatchType.Code convertTermMatchTypeToProto( - @AppSearchSchema.StringPropertyConfig.IndexingType int indexingType) { - switch (indexingType) { - case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE: - return TermMatchType.Code.UNKNOWN; - case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS: - return TermMatchType.Code.EXACT_ONLY; - case AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES: - return TermMatchType.Code.PREFIX; - default: - throw new IllegalArgumentException("Invalid indexingType: " + indexingType); - } - } - - @AppSearchSchema.StringPropertyConfig.IndexingType - private static int convertTermMatchTypeFromProto(@NonNull TermMatchType.Code termMatchType) { - switch (termMatchType) { - case UNKNOWN: - return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE; - case EXACT_ONLY: - return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS; - case PREFIX: - return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_PREFIXES; - default: - // Avoid crashing in the 'read' path; we should try to interpret the document to the - // extent possible. - Log.w(TAG, "Invalid indexingType: " + termMatchType.getNumber()); - return AppSearchSchema.StringPropertyConfig.INDEXING_TYPE_NONE; - } - } - - @NonNull - private static StringIndexingConfig.TokenizerType.Code convertTokenizerTypeToProto( - @AppSearchSchema.StringPropertyConfig.TokenizerType int tokenizerType) { - StringIndexingConfig.TokenizerType.Code tokenizerTypeProto = - StringIndexingConfig.TokenizerType.Code.forNumber(tokenizerType); - if (tokenizerTypeProto == null) { - throw new IllegalArgumentException("Invalid tokenizerType: " + tokenizerType); - } - return tokenizerTypeProto; - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java deleted file mode 100644 index 6b443b3852e19..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java +++ /dev/null @@ -1,141 +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 com.android.server.appsearch.external.localstorage.converter; - -import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix; - -import android.annotation.NonNull; -import android.app.appsearch.GenericDocument; -import android.app.appsearch.SearchResult; -import android.app.appsearch.SearchResultPage; -import android.os.Bundle; - -import com.android.internal.util.Preconditions; - -import com.google.android.icing.proto.SchemaTypeConfigProto; -import com.google.android.icing.proto.SearchResultProto; -import com.google.android.icing.proto.SearchResultProtoOrBuilder; -import com.google.android.icing.proto.SnippetMatchProto; -import com.google.android.icing.proto.SnippetProto; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * Translates a {@link SearchResultProto} into {@link SearchResult}s. - * - * @hide - */ -public class SearchResultToProtoConverter { - private SearchResultToProtoConverter() {} - - /** - * Translate a {@link SearchResultProto} into {@link SearchResultPage}. - * - * @param proto The {@link SearchResultProto} containing results. - * @param packageNames A parallel array of package names. The package name at index 'i' of this - * list should be the package that indexed the document at index 'i' of proto.getResults(i). - * @param databaseNames A parallel array of database names. The database name at index 'i' of - * this list shold be the database that indexed the document at index 'i' of - * proto.getResults(i). - * @param schemaMap A map of prefixes to an inner-map of prefixed schema type to - * SchemaTypeConfigProtos, used for setting a default value for results with DocumentProtos - * that have empty values. - * @return {@link SearchResultPage} of results. - */ - @NonNull - public static SearchResultPage toSearchResultPage( - @NonNull SearchResultProtoOrBuilder proto, - @NonNull List packageNames, - @NonNull List databaseNames, - @NonNull Map> schemaMap) { - Preconditions.checkArgument( - proto.getResultsCount() == packageNames.size(), - "Size of results does not match the number of package names."); - Bundle bundle = new Bundle(); - bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken()); - ArrayList resultBundles = new ArrayList<>(proto.getResultsCount()); - for (int i = 0; i < proto.getResultsCount(); i++) { - String prefix = createPrefix(packageNames.get(i), databaseNames.get(i)); - Map schemaTypeMap = schemaMap.get(prefix); - SearchResult result = - toSearchResult( - proto.getResults(i), - packageNames.get(i), - databaseNames.get(i), - schemaTypeMap); - resultBundles.add(result.getBundle()); - } - bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles); - return new SearchResultPage(bundle); - } - - /** - * Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. - * - * @param proto The proto to be converted. - * @param packageName The package name associated with the document in {@code proto}. - * @param databaseName The database name associated with the document in {@code proto}. - * @param schemaTypeToProtoMap A map of prefixed schema types to their corresponding - * SchemaTypeConfigProto, used for setting a default value for results with DocumentProtos - * that have empty values. - * @return A {@link SearchResult} bundle. - */ - @NonNull - private static SearchResult toSearchResult( - @NonNull SearchResultProto.ResultProtoOrBuilder proto, - @NonNull String packageName, - @NonNull String databaseName, - @NonNull Map schemaTypeToProtoMap) { - String prefix = createPrefix(packageName, databaseName); - GenericDocument document = - GenericDocumentToProtoConverter.toGenericDocument( - proto.getDocument(), prefix, schemaTypeToProtoMap); - SearchResult.Builder builder = - new SearchResult.Builder(packageName, databaseName) - .setGenericDocument(document) - .setRankingSignal(proto.getScore()); - if (proto.hasSnippet()) { - for (int i = 0; i < proto.getSnippet().getEntriesCount(); i++) { - SnippetProto.EntryProto entry = proto.getSnippet().getEntries(i); - for (int j = 0; j < entry.getSnippetMatchesCount(); j++) { - SearchResult.MatchInfo matchInfo = - toMatchInfo(entry.getSnippetMatches(j), entry.getPropertyName()); - builder.addMatchInfo(matchInfo); - } - } - } - return builder.build(); - } - - private static SearchResult.MatchInfo toMatchInfo( - @NonNull SnippetMatchProto snippetMatchProto, @NonNull String propertyPath) { - return new SearchResult.MatchInfo.Builder(propertyPath) - .setExactMatchRange( - new SearchResult.MatchRange( - snippetMatchProto.getExactMatchUtf16Position(), - snippetMatchProto.getExactMatchUtf16Position() - + snippetMatchProto.getExactMatchUtf16Length())) - .setSnippetRange( - new SearchResult.MatchRange( - snippetMatchProto.getWindowUtf16Position(), - snippetMatchProto.getWindowUtf16Position() - + snippetMatchProto.getWindowUtf16Length())) - .build(); - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java deleted file mode 100644 index 8f9e9bdc8cbc4..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchSpecToProtoConverter.java +++ /dev/null @@ -1,116 +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 com.android.server.appsearch.external.localstorage.converter; - -import android.annotation.NonNull; -import android.app.appsearch.SearchSpec; - -import com.google.android.icing.proto.ResultSpecProto; -import com.google.android.icing.proto.ScoringSpecProto; -import com.google.android.icing.proto.SearchSpecProto; -import com.google.android.icing.proto.TermMatchType; - -import java.util.Objects; - -/** - * Translates a {@link SearchSpec} into icing search protos. - * - * @hide - */ -public final class SearchSpecToProtoConverter { - private SearchSpecToProtoConverter() {} - - /** Extracts {@link SearchSpecProto} information from a {@link SearchSpec}. */ - @NonNull - public static SearchSpecProto toSearchSpecProto(@NonNull SearchSpec spec) { - Objects.requireNonNull(spec); - SearchSpecProto.Builder protoBuilder = - SearchSpecProto.newBuilder() - .addAllSchemaTypeFilters(spec.getFilterSchemas()) - .addAllNamespaceFilters(spec.getFilterNamespaces()); - - @SearchSpec.TermMatch int termMatchCode = spec.getTermMatch(); - TermMatchType.Code termMatchCodeProto = TermMatchType.Code.forNumber(termMatchCode); - if (termMatchCodeProto == null || termMatchCodeProto.equals(TermMatchType.Code.UNKNOWN)) { - throw new IllegalArgumentException("Invalid term match type: " + termMatchCode); - } - protoBuilder.setTermMatchType(termMatchCodeProto); - - return protoBuilder.build(); - } - - /** Extracts {@link ResultSpecProto} information from a {@link SearchSpec}. */ - @NonNull - public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) { - Objects.requireNonNull(spec); - return ResultSpecProto.newBuilder() - .setNumPerPage(spec.getResultCountPerPage()) - .setSnippetSpec( - ResultSpecProto.SnippetSpecProto.newBuilder() - .setNumToSnippet(spec.getSnippetCount()) - .setNumMatchesPerProperty(spec.getSnippetCountPerProperty()) - .setMaxWindowBytes(spec.getMaxSnippetSize())) - .addAllTypePropertyMasks( - TypePropertyPathToProtoConverter.toTypePropertyMaskList( - spec.getProjections())) - .build(); - } - - /** Extracts {@link ScoringSpecProto} information from a {@link SearchSpec}. */ - @NonNull - public static ScoringSpecProto toScoringSpecProto(@NonNull SearchSpec spec) { - Objects.requireNonNull(spec); - ScoringSpecProto.Builder protoBuilder = ScoringSpecProto.newBuilder(); - - @SearchSpec.Order int orderCode = spec.getOrder(); - ScoringSpecProto.Order.Code orderCodeProto = - ScoringSpecProto.Order.Code.forNumber(orderCode); - if (orderCodeProto == null) { - throw new IllegalArgumentException("Invalid result ranking order: " + orderCode); - } - protoBuilder - .setOrderBy(orderCodeProto) - .setRankBy(toProtoRankingStrategy(spec.getRankingStrategy())); - - return protoBuilder.build(); - } - - private static ScoringSpecProto.RankingStrategy.Code toProtoRankingStrategy( - @SearchSpec.RankingStrategy int rankingStrategyCode) { - switch (rankingStrategyCode) { - case SearchSpec.RANKING_STRATEGY_NONE: - return ScoringSpecProto.RankingStrategy.Code.NONE; - case SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE: - return ScoringSpecProto.RankingStrategy.Code.DOCUMENT_SCORE; - case SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP: - return ScoringSpecProto.RankingStrategy.Code.CREATION_TIMESTAMP; - case SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE: - return ScoringSpecProto.RankingStrategy.Code.RELEVANCE_SCORE; - case SearchSpec.RANKING_STRATEGY_USAGE_COUNT: - return ScoringSpecProto.RankingStrategy.Code.USAGE_TYPE1_COUNT; - case SearchSpec.RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP: - return ScoringSpecProto.RankingStrategy.Code.USAGE_TYPE1_LAST_USED_TIMESTAMP; - case SearchSpec.RANKING_STRATEGY_SYSTEM_USAGE_COUNT: - return ScoringSpecProto.RankingStrategy.Code.USAGE_TYPE2_COUNT; - case SearchSpec.RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP: - return ScoringSpecProto.RankingStrategy.Code.USAGE_TYPE2_LAST_USED_TIMESTAMP; - default: - throw new IllegalArgumentException( - "Invalid result ranking strategy: " + rankingStrategyCode); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java deleted file mode 100644 index ed73593fd1a20..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SetSchemaResponseToProtoConverter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage.converter; - -import android.annotation.NonNull; -import android.app.appsearch.SetSchemaResponse; - -import com.google.android.icing.proto.SetSchemaResultProto; - -import java.util.Objects; - -/** - * Translates a {@link SetSchemaResultProto} into {@link SetSchemaResponse}. - * - * @hide - */ -public class SetSchemaResponseToProtoConverter { - - private SetSchemaResponseToProtoConverter() {} - - /** - * Translate a {@link SetSchemaResultProto} into {@link SetSchemaResponse}. - * - * @param proto The {@link SetSchemaResultProto} containing results. - * @param prefix The prefix need to removed from schemaTypes - * @return The {@link SetSchemaResponse} object. - */ - @NonNull - public static SetSchemaResponse toSetSchemaResponse( - @NonNull SetSchemaResultProto proto, @NonNull String prefix) { - Objects.requireNonNull(proto); - Objects.requireNonNull(prefix); - SetSchemaResponse.Builder builder = new SetSchemaResponse.Builder(); - - for (int i = 0; i < proto.getDeletedSchemaTypesCount(); i++) { - builder.addDeletedType(proto.getDeletedSchemaTypes(i).substring(prefix.length())); - } - - for (int i = 0; i < proto.getIncompatibleSchemaTypesCount(); i++) { - builder.addIncompatibleType( - proto.getIncompatibleSchemaTypes(i).substring(prefix.length())); - } - - return builder.build(); - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java deleted file mode 100644 index acf04ef08a2c7..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/TypePropertyPathToProtoConverter.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage.converter; - -import android.annotation.NonNull; - -import com.google.android.icing.proto.TypePropertyMask; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * Translates a Map> into List. - * - * @hide - */ -public final class TypePropertyPathToProtoConverter { - private TypePropertyPathToProtoConverter() {} - - /** Extracts {@link TypePropertyMask} information from a {@link Map}. */ - @NonNull - public static List toTypePropertyMaskList( - @NonNull Map> typePropertyPaths) { - Objects.requireNonNull(typePropertyPaths); - List typePropertyMasks = new ArrayList<>(typePropertyPaths.size()); - for (Map.Entry> e : typePropertyPaths.entrySet()) { - typePropertyMasks.add( - TypePropertyMask.newBuilder() - .setSchemaType(e.getKey()) - .addAllPaths(e.getValue()) - .build()); - } - return typePropertyMasks; - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java deleted file mode 100644 index 81fb418a6a0a5..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/CallStats.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage.stats; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.AppSearchResult; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * A class for setting basic information to log for all function calls. - * - *

This class can set which stats to log for both batch and non-batch {@link - * android.app.appsearch.AppSearchSession} calls. - * - *

Some function calls may have their own detailed stats class like {@link PutDocumentStats}. - * However, {@link CallStats} can still be used along with the detailed stats class for easy - * aggregation/analysis with other function calls. - * - * @hide - */ -public class CallStats { - @IntDef( - value = { - CALL_TYPE_UNKNOWN, - CALL_TYPE_INITIALIZE, - CALL_TYPE_SET_SCHEMA, - CALL_TYPE_PUT_DOCUMENTS, - CALL_TYPE_GET_DOCUMENTS, - CALL_TYPE_REMOVE_DOCUMENTS_BY_ID, - CALL_TYPE_PUT_DOCUMENT, - CALL_TYPE_GET_DOCUMENT, - CALL_TYPE_REMOVE_DOCUMENT_BY_ID, - CALL_TYPE_SEARCH, - CALL_TYPE_OPTIMIZE, - CALL_TYPE_FLUSH, - CALL_TYPE_GLOBAL_SEARCH, - CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH, - CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface CallType {} - - public static final int CALL_TYPE_UNKNOWN = 0; - public static final int CALL_TYPE_INITIALIZE = 1; - public static final int CALL_TYPE_SET_SCHEMA = 2; - public static final int CALL_TYPE_PUT_DOCUMENTS = 3; - public static final int CALL_TYPE_GET_DOCUMENTS = 4; - public static final int CALL_TYPE_REMOVE_DOCUMENTS_BY_ID = 5; - public static final int CALL_TYPE_PUT_DOCUMENT = 6; - public static final int CALL_TYPE_GET_DOCUMENT = 7; - public static final int CALL_TYPE_REMOVE_DOCUMENT_BY_ID = 8; - public static final int CALL_TYPE_SEARCH = 9; - public static final int CALL_TYPE_OPTIMIZE = 10; - public static final int CALL_TYPE_FLUSH = 11; - public static final int CALL_TYPE_GLOBAL_SEARCH = 12; - public static final int CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH = 13; - public static final int CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH = 14; - - @Nullable private final String mPackageName; - @Nullable private final String mDatabase; - /** - * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal - * state. - */ - @AppSearchResult.ResultCode private final int mStatusCode; - - private final int mTotalLatencyMillis; - - @CallType private final int mCallType; - private final int mEstimatedBinderLatencyMillis; - private final int mNumOperationsSucceeded; - private final int mNumOperationsFailed; - - CallStats(@NonNull Builder builder) { - Objects.requireNonNull(builder); - mPackageName = builder.mPackageName; - mDatabase = builder.mDatabase; - mStatusCode = builder.mStatusCode; - mTotalLatencyMillis = builder.mTotalLatencyMillis; - mCallType = builder.mCallType; - mEstimatedBinderLatencyMillis = builder.mEstimatedBinderLatencyMillis; - mNumOperationsSucceeded = builder.mNumOperationsSucceeded; - mNumOperationsFailed = builder.mNumOperationsFailed; - } - - /** Returns calling package name. */ - @Nullable - public String getPackageName() { - return mPackageName; - } - - /** Returns calling database name. */ - @Nullable - public String getDatabase() { - return mDatabase; - } - - /** Returns status code for this api call. */ - @AppSearchResult.ResultCode - public int getStatusCode() { - return mStatusCode; - } - - /** Returns total latency of this api call in millis. */ - public int getTotalLatencyMillis() { - return mTotalLatencyMillis; - } - - /** Returns type of the call. */ - @CallType - public int getCallType() { - return mCallType; - } - - /** Returns estimated binder latency, in milliseconds */ - public int getEstimatedBinderLatencyMillis() { - return mEstimatedBinderLatencyMillis; - } - - /** - * Returns number of operations succeeded. - * - *

For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total - * number of individual successful put operations. In this case, how many documents are - * successfully indexed. - * - *

For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema}, the - * sum of {@link CallStats#getNumOperationsSucceeded()} and {@link - * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation. - */ - public int getNumOperationsSucceeded() { - return mNumOperationsSucceeded; - } - - /** - * Returns number of operations failed. - * - *

For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total - * number of individual failed put operations. In this case, how many documents are failed to be - * indexed. - * - *

For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema}, the - * sum of {@link CallStats#getNumOperationsSucceeded()} and {@link - * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation. - */ - public int getNumOperationsFailed() { - return mNumOperationsFailed; - } - - /** Builder for {@link CallStats}. */ - public static class Builder { - @Nullable String mPackageName; - @Nullable String mDatabase; - @AppSearchResult.ResultCode int mStatusCode; - int mTotalLatencyMillis; - @CallType int mCallType; - int mEstimatedBinderLatencyMillis; - int mNumOperationsSucceeded; - int mNumOperationsFailed; - - /** Sets the PackageName used by the session. */ - @NonNull - public Builder setPackageName(@NonNull String packageName) { - mPackageName = Objects.requireNonNull(packageName); - return this; - } - - /** Sets the database used by the session. */ - @NonNull - public Builder setDatabase(@NonNull String database) { - mDatabase = Objects.requireNonNull(database); - return this; - } - - /** Sets the status code. */ - @NonNull - public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) { - mStatusCode = statusCode; - return this; - } - - /** Sets total latency in millis. */ - @NonNull - public Builder setTotalLatencyMillis(int totalLatencyMillis) { - mTotalLatencyMillis = totalLatencyMillis; - return this; - } - - /** Sets type of the call. */ - @NonNull - public Builder setCallType(@CallType int callType) { - mCallType = callType; - return this; - } - - /** Sets estimated binder latency, in milliseconds. */ - @NonNull - public Builder setEstimatedBinderLatencyMillis(int estimatedBinderLatencyMillis) { - mEstimatedBinderLatencyMillis = estimatedBinderLatencyMillis; - return this; - } - - /** - * Sets number of operations succeeded. - * - *

For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total - * number of individual successful put operations. In this case, how many documents are - * successfully indexed. - * - *

For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema}, - * the sum of {@link CallStats#getNumOperationsSucceeded()} and {@link - * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation. - */ - @NonNull - public Builder setNumOperationsSucceeded(int numOperationsSucceeded) { - mNumOperationsSucceeded = numOperationsSucceeded; - return this; - } - - /** - * Sets number of operations failed. - * - *

For example, for {@link android.app.appsearch.AppSearchSession#put}, it is the total - * number of individual failed put operations. In this case, how many documents are failed - * to be indexed. - * - *

For non-batch calls such as {@link android.app.appsearch.AppSearchSession#setSchema}, - * the sum of {@link CallStats#getNumOperationsSucceeded()} and {@link - * CallStats#getNumOperationsFailed()} is always 1 since there is only one operation. - */ - @NonNull - public Builder setNumOperationsFailed(int numOperationsFailed) { - mNumOperationsFailed = numOperationsFailed; - return this; - } - - /** Creates {@link CallStats} object from {@link Builder} instance. */ - @NonNull - public CallStats build() { - return new CallStats(/* builder= */ this); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java deleted file mode 100644 index 72befa795d9fb..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/InitializeStats.java +++ /dev/null @@ -1,442 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage.stats; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.app.appsearch.AppSearchResult; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * Class holds detailed stats for initialization - * - * @hide - */ -public final class InitializeStats { - /** - * The cause of IcingSearchEngine recovering from a previous bad state during initialization. - */ - @IntDef( - value = { - // It needs to be sync with RecoveryCause in - // external/icing/proto/icing/proto/logging.proto#InitializeStatsProto - RECOVERY_CAUSE_NONE, - RECOVERY_CAUSE_DATA_LOSS, - RECOVERY_CAUSE_INCONSISTENT_WITH_GROUND_TRUTH, - RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH, - RECOVERY_CAUSE_IO_ERROR, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface RecoveryCause {} - - // No recovery happened. - public static final int RECOVERY_CAUSE_NONE = 0; - // Data loss in ground truth. - public static final int RECOVERY_CAUSE_DATA_LOSS = 1; - // Data in index is inconsistent with ground truth. - public static final int RECOVERY_CAUSE_INCONSISTENT_WITH_GROUND_TRUTH = 2; - // Total checksum of all the components does not match. - public static final int RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH = 3; - // Random I/O errors. - public static final int RECOVERY_CAUSE_IO_ERROR = 4; - - /** Status regarding how much data is lost during the initialization. */ - @IntDef( - value = { - // It needs to be sync with DocumentStoreDataStatus in - // external/icing/proto/icing/proto/logging.proto#InitializeStatsProto - - DOCUMENT_STORE_DATA_STATUS_NO_DATA_LOSS, - DOCUMENT_STORE_DATA_STATUS_PARTIAL_LOSS, - DOCUMENT_STORE_DATA_STATUS_COMPLETE_LOSS, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DocumentStoreDataStatus {} - - // Document store is successfully initialized or fully recovered. - public static final int DOCUMENT_STORE_DATA_STATUS_NO_DATA_LOSS = 0; - // Ground truth data is partially lost. - public static final int DOCUMENT_STORE_DATA_STATUS_PARTIAL_LOSS = 1; - // Ground truth data is completely lost. - public static final int DOCUMENT_STORE_DATA_STATUS_COMPLETE_LOSS = 2; - - @AppSearchResult.ResultCode private final int mStatusCode; - private final int mTotalLatencyMillis; - /** Whether the initialize() detects deSync. */ - private final boolean mHasDeSync; - /** Time used to read and process the schema and namespaces. */ - private final int mPrepareSchemaAndNamespacesLatencyMillis; - /** Time used to read and process the visibility store. */ - private final int mPrepareVisibilityStoreLatencyMillis; - /** Overall time used for the native function call. */ - private final int mNativeLatencyMillis; - - @RecoveryCause private final int mNativeDocumentStoreRecoveryCause; - @RecoveryCause private final int mNativeIndexRestorationCause; - @RecoveryCause private final int mNativeSchemaStoreRecoveryCause; - /** Time used to recover the document store. */ - private final int mNativeDocumentStoreRecoveryLatencyMillis; - /** Time used to restore the index. */ - private final int mNativeIndexRestorationLatencyMillis; - /** Time used to recover the schema store. */ - private final int mNativeSchemaStoreRecoveryLatencyMillis; - /** Status regarding how much data is lost during the initialization. */ - private final int mNativeDocumentStoreDataStatus; - /** - * Returns number of documents currently in document store. Those may include alive, deleted, - * and expired documents. - */ - private final int mNativeNumDocuments; - /** Returns number of schema types currently in the schema store. */ - 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. */ - @AppSearchResult.ResultCode - public int getStatusCode() { - return mStatusCode; - } - - /** Returns the total latency in milliseconds for the initialization. */ - public int getTotalLatencyMillis() { - return mTotalLatencyMillis; - } - - /** - * Returns whether the initialize() detects deSync. - * - *

If there is a deSync, it means AppSearch and IcingSearchEngine have an inconsistent view - * of what data should exist. - */ - public boolean hasDeSync() { - return mHasDeSync; - } - - /** Returns time used to read and process the schema and namespaces. */ - public int getPrepareSchemaAndNamespacesLatencyMillis() { - return mPrepareSchemaAndNamespacesLatencyMillis; - } - - /** Returns time used to read and process the visibility file. */ - public int getPrepareVisibilityStoreLatencyMillis() { - return mPrepareVisibilityStoreLatencyMillis; - } - - /** Returns overall time used for the native function call. */ - public int getNativeLatencyMillis() { - return mNativeLatencyMillis; - } - - /** - * Returns recovery cause for document store. - * - *

Possible recovery causes for document store: - *

  • {@link InitializeStats#RECOVERY_CAUSE_DATA_LOSS} - *
  • {@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH} - *
  • {@link InitializeStats#RECOVERY_CAUSE_IO_ERROR} - */ - @RecoveryCause - public int getDocumentStoreRecoveryCause() { - return mNativeDocumentStoreRecoveryCause; - } - - /** - * Returns restoration cause for index store. - * - *

    Possible causes: - *

  • {@link InitializeStats#RECOVERY_CAUSE_INCONSISTENT_WITH_GROUND_TRUTH} - *
  • {@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH} - *
  • {@link InitializeStats#RECOVERY_CAUSE_IO_ERROR} - */ - @RecoveryCause - public int getIndexRestorationCause() { - return mNativeIndexRestorationCause; - } - - /** - * Returns recovery cause for schema store. - * - *

    Possible causes: - *

  • IO_ERROR - */ - @RecoveryCause - public int getSchemaStoreRecoveryCause() { - return mNativeSchemaStoreRecoveryCause; - } - - /** Returns time used to recover the document store. */ - public int getDocumentStoreRecoveryLatencyMillis() { - return mNativeDocumentStoreRecoveryLatencyMillis; - } - - /** Returns time used to restore the index. */ - public int getIndexRestorationLatencyMillis() { - return mNativeIndexRestorationLatencyMillis; - } - - /** Returns time used to recover the schema store. */ - public int getSchemaStoreRecoveryLatencyMillis() { - return mNativeSchemaStoreRecoveryLatencyMillis; - } - - /** Returns status about how much data is lost during the initialization. */ - @DocumentStoreDataStatus - public int getDocumentStoreDataStatus() { - return mNativeDocumentStoreDataStatus; - } - - /** - * Returns number of documents currently in document store. Those may include alive, deleted, - * and expired documents. - */ - public int getDocumentCount() { - return mNativeNumDocuments; - } - - /** Returns number of schema types currently in the schema store. */ - public int getSchemaTypeCount() { - 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}. - * - *

    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) { - Objects.requireNonNull(builder); - mStatusCode = builder.mStatusCode; - mTotalLatencyMillis = builder.mTotalLatencyMillis; - mHasDeSync = builder.mHasDeSync; - mPrepareSchemaAndNamespacesLatencyMillis = builder.mPrepareSchemaAndNamespacesLatencyMillis; - mPrepareVisibilityStoreLatencyMillis = builder.mPrepareVisibilityStoreLatencyMillis; - mNativeLatencyMillis = builder.mNativeLatencyMillis; - mNativeDocumentStoreRecoveryCause = builder.mNativeDocumentStoreRecoveryCause; - mNativeIndexRestorationCause = builder.mNativeIndexRestorationCause; - mNativeSchemaStoreRecoveryCause = builder.mNativeSchemaStoreRecoveryCause; - mNativeDocumentStoreRecoveryLatencyMillis = - builder.mNativeDocumentStoreRecoveryLatencyMillis; - mNativeIndexRestorationLatencyMillis = builder.mNativeIndexRestorationLatencyMillis; - mNativeSchemaStoreRecoveryLatencyMillis = builder.mNativeSchemaStoreRecoveryLatencyMillis; - mNativeDocumentStoreDataStatus = builder.mNativeDocumentStoreDataStatus; - mNativeNumDocuments = builder.mNativeNumDocuments; - mNativeNumSchemaTypes = builder.mNativeNumSchemaTypes; - mHasReset = builder.mHasReset; - mResetStatusCode = builder.mResetStatusCode; - } - - /** Builder for {@link InitializeStats}. */ - public static class Builder { - @AppSearchResult.ResultCode int mStatusCode; - - int mTotalLatencyMillis; - boolean mHasDeSync; - int mPrepareSchemaAndNamespacesLatencyMillis; - int mPrepareVisibilityStoreLatencyMillis; - int mNativeLatencyMillis; - @RecoveryCause int mNativeDocumentStoreRecoveryCause; - @RecoveryCause int mNativeIndexRestorationCause; - @RecoveryCause int mNativeSchemaStoreRecoveryCause; - int mNativeDocumentStoreRecoveryLatencyMillis; - int mNativeIndexRestorationLatencyMillis; - int mNativeSchemaStoreRecoveryLatencyMillis; - @DocumentStoreDataStatus int mNativeDocumentStoreDataStatus; - int mNativeNumDocuments; - int mNativeNumSchemaTypes; - boolean mHasReset; - @AppSearchResult.ResultCode int mResetStatusCode; - - /** Sets the status of the initialization. */ - @NonNull - public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) { - mStatusCode = statusCode; - return this; - } - - /** Sets the total latency of the initialization in milliseconds. */ - @NonNull - public Builder setTotalLatencyMillis(int totalLatencyMillis) { - mTotalLatencyMillis = totalLatencyMillis; - return this; - } - - /** - * Sets whether the initialize() detects deSync. - * - *

    If there is a deSync, it means AppSearch and IcingSearchEngine have an inconsistent - * view of what data should exist. - */ - @NonNull - public Builder setHasDeSync(boolean hasDeSync) { - mHasDeSync = hasDeSync; - return this; - } - - /** Sets time used to read and process the schema and namespaces. */ - @NonNull - public Builder setPrepareSchemaAndNamespacesLatencyMillis( - int prepareSchemaAndNamespacesLatencyMillis) { - mPrepareSchemaAndNamespacesLatencyMillis = prepareSchemaAndNamespacesLatencyMillis; - return this; - } - - /** Sets time used to read and process the visibility file. */ - @NonNull - public Builder setPrepareVisibilityStoreLatencyMillis( - int prepareVisibilityStoreLatencyMillis) { - mPrepareVisibilityStoreLatencyMillis = prepareVisibilityStoreLatencyMillis; - return this; - } - - /** Sets overall time used for the native function call. */ - @NonNull - public Builder setNativeLatencyMillis(int nativeLatencyMillis) { - mNativeLatencyMillis = nativeLatencyMillis; - return this; - } - - /** - * Sets recovery cause for document store. - * - *

    Possible recovery causes for document store: - *

  • {@link InitializeStats#RECOVERY_CAUSE_DATA_LOSS} - *
  • {@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH} - *
  • {@link InitializeStats#RECOVERY_CAUSE_IO_ERROR} - */ - @NonNull - public Builder setDocumentStoreRecoveryCause( - @RecoveryCause int documentStoreRecoveryCause) { - mNativeDocumentStoreRecoveryCause = documentStoreRecoveryCause; - return this; - } - - /** - * Sets restoration cause for index store. - * - *

    Possible causes: - *

  • {@link InitializeStats#DOCUMENT_STORE_DATA_STATUS_COMPLETE_LOSS} - *
  • {@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH} - *
  • {@link InitializeStats#RECOVERY_CAUSE_IO_ERROR} - */ - @NonNull - public Builder setIndexRestorationCause(@RecoveryCause int indexRestorationCause) { - mNativeIndexRestorationCause = indexRestorationCause; - return this; - } - - /** - * Returns recovery cause for schema store. - * - *

    Possible causes: - *

  • {@link InitializeStats#RECOVERY_CAUSE_IO_ERROR} - */ - @NonNull - public Builder setSchemaStoreRecoveryCause(@RecoveryCause int schemaStoreRecoveryCause) { - mNativeSchemaStoreRecoveryCause = schemaStoreRecoveryCause; - return this; - } - - /** Sets time used to recover the document store. */ - @NonNull - public Builder setDocumentStoreRecoveryLatencyMillis( - int documentStoreRecoveryLatencyMillis) { - mNativeDocumentStoreRecoveryLatencyMillis = documentStoreRecoveryLatencyMillis; - return this; - } - - /** Sets time used to restore the index. */ - @NonNull - public Builder setIndexRestorationLatencyMillis(int indexRestorationLatencyMillis) { - mNativeIndexRestorationLatencyMillis = indexRestorationLatencyMillis; - return this; - } - - /** Sets time used to recover the schema store. */ - @NonNull - public Builder setSchemaStoreRecoveryLatencyMillis(int schemaStoreRecoveryLatencyMillis) { - mNativeSchemaStoreRecoveryLatencyMillis = schemaStoreRecoveryLatencyMillis; - return this; - } - - /** - * Sets Native Document Store Data status. status is defined in - * external/icing/proto/icing/proto/logging.proto - */ - @NonNull - public Builder setDocumentStoreDataStatus( - @DocumentStoreDataStatus int documentStoreDataStatus) { - mNativeDocumentStoreDataStatus = documentStoreDataStatus; - return this; - } - - /** - * Sets number of documents currently in document store. Those may include alive, deleted, - * and expired documents. - */ - @NonNull - public Builder setDocumentCount(int numDocuments) { - mNativeNumDocuments = numDocuments; - return this; - } - - /** Sets number of schema types currently in the schema store. */ - @NonNull - public Builder setSchemaTypeCount(int numSchemaTypes) { - mNativeNumSchemaTypes = numSchemaTypes; - 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 - * InitializeStats.Builder} - */ - @NonNull - public InitializeStats build() { - return new InitializeStats(/* builder= */ this); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/OptimizeStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/OptimizeStats.java deleted file mode 100644 index 83bd50f92e3b6..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/OptimizeStats.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage.stats; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchResult; - -import java.util.Objects; - -/** - * Class holds detailed stats for Optimize. - * - * @hide - */ -public final class OptimizeStats { - /** - * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal - * state. - */ - @AppSearchResult.ResultCode private final int mStatusCode; - - private final int mTotalLatencyMillis; - private final int mNativeLatencyMillis; - - // Time used to optimize the document store in millis. - private final int mNativeDocumentStoreOptimizeLatencyMillis; - - // Time used to restore the index in millis. - private final int mNativeIndexRestorationLatencyMillis; - - // Number of documents before the optimization. - private final int mNativeOriginalDocumentCount; - - // Number of documents deleted during the optimization. - private final int mNativeDeletedDocumentCount; - - // Number of documents expired during the optimization. - private final int mNativeExpiredDocumentCount; - - // Size of storage in bytes before the optimization. - private final long mNativeStorageSizeBeforeBytes; - - // Size of storage in bytes after the optimization. - private final long mNativeStorageSizeAfterBytes; - - // The amount of time in millis since the last optimization ran calculated using wall clock time - private final long mNativeTimeSinceLastOptimizeMillis; - - OptimizeStats(@NonNull Builder builder) { - Objects.requireNonNull(builder); - mStatusCode = builder.mStatusCode; - mTotalLatencyMillis = builder.mTotalLatencyMillis; - mNativeLatencyMillis = builder.mNativeLatencyMillis; - mNativeDocumentStoreOptimizeLatencyMillis = - builder.mNativeDocumentStoreOptimizeLatencyMillis; - mNativeIndexRestorationLatencyMillis = builder.mNativeIndexRestorationLatencyMillis; - mNativeOriginalDocumentCount = builder.mNativeOriginalDocumentCount; - mNativeDeletedDocumentCount = builder.mNativeDeletedDocumentCount; - mNativeExpiredDocumentCount = builder.mNativeExpiredDocumentCount; - mNativeStorageSizeBeforeBytes = builder.mNativeStorageSizeBeforeBytes; - mNativeStorageSizeAfterBytes = builder.mNativeStorageSizeAfterBytes; - mNativeTimeSinceLastOptimizeMillis = builder.mNativeTimeSinceLastOptimizeMillis; - } - - /** Returns status code for this optimization. */ - @AppSearchResult.ResultCode - public int getStatusCode() { - return mStatusCode; - } - - /** Returns total latency of this optimization in millis. */ - public int getTotalLatencyMillis() { - return mTotalLatencyMillis; - } - - /** Returns how much time in millis spent in the native code. */ - public int getNativeLatencyMillis() { - return mNativeLatencyMillis; - } - - /** Returns time used to optimize the document store in millis. */ - public int getDocumentStoreOptimizeLatencyMillis() { - return mNativeDocumentStoreOptimizeLatencyMillis; - } - - /** Returns time used to restore the index in millis. */ - public int getIndexRestorationLatencyMillis() { - return mNativeIndexRestorationLatencyMillis; - } - - /** Returns number of documents before the optimization. */ - public int getOriginalDocumentCount() { - return mNativeOriginalDocumentCount; - } - - /** Returns number of documents deleted during the optimization. */ - public int getDeletedDocumentCount() { - return mNativeDeletedDocumentCount; - } - - /** Returns number of documents expired during the optimization. */ - public int getExpiredDocumentCount() { - return mNativeExpiredDocumentCount; - } - - /** Returns size of storage in bytes before the optimization. */ - public long getStorageSizeBeforeBytes() { - return mNativeStorageSizeBeforeBytes; - } - - /** Returns size of storage in bytes after the optimization. */ - public long getStorageSizeAfterBytes() { - return mNativeStorageSizeAfterBytes; - } - - /** - * Returns the amount of time in millis since the last optimization ran calculated using wall - * clock time. - */ - public long getTimeSinceLastOptimizeMillis() { - return mNativeTimeSinceLastOptimizeMillis; - } - - /** Builder for {@link RemoveStats}. */ - public static class Builder { - /** - * The status code returned by {@link AppSearchResult#getResultCode()} for the call or - * internal state. - */ - @AppSearchResult.ResultCode int mStatusCode; - - int mTotalLatencyMillis; - int mNativeLatencyMillis; - int mNativeDocumentStoreOptimizeLatencyMillis; - int mNativeIndexRestorationLatencyMillis; - int mNativeOriginalDocumentCount; - int mNativeDeletedDocumentCount; - int mNativeExpiredDocumentCount; - long mNativeStorageSizeBeforeBytes; - long mNativeStorageSizeAfterBytes; - long mNativeTimeSinceLastOptimizeMillis; - - /** Sets the status code. */ - @NonNull - public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) { - mStatusCode = statusCode; - return this; - } - - /** Sets total latency in millis. */ - @NonNull - public Builder setTotalLatencyMillis(int totalLatencyMillis) { - mTotalLatencyMillis = totalLatencyMillis; - return this; - } - - /** Sets native latency in millis. */ - @NonNull - public Builder setNativeLatencyMillis(int nativeLatencyMillis) { - mNativeLatencyMillis = nativeLatencyMillis; - return this; - } - - /** Sets time used to optimize the document store. */ - @NonNull - public Builder setDocumentStoreOptimizeLatencyMillis( - int documentStoreOptimizeLatencyMillis) { - mNativeDocumentStoreOptimizeLatencyMillis = documentStoreOptimizeLatencyMillis; - return this; - } - - /** Sets time used to restore the index. */ - @NonNull - public Builder setIndexRestorationLatencyMillis(int indexRestorationLatencyMillis) { - mNativeIndexRestorationLatencyMillis = indexRestorationLatencyMillis; - return this; - } - - /** Sets number of documents before the optimization. */ - @NonNull - public Builder setOriginalDocumentCount(int originalDocumentCount) { - mNativeOriginalDocumentCount = originalDocumentCount; - return this; - } - - /** Sets number of documents deleted during the optimization. */ - @NonNull - public Builder setDeletedDocumentCount(int deletedDocumentCount) { - mNativeDeletedDocumentCount = deletedDocumentCount; - return this; - } - - /** Sets number of documents expired during the optimization. */ - @NonNull - public Builder setExpiredDocumentCount(int expiredDocumentCount) { - mNativeExpiredDocumentCount = expiredDocumentCount; - return this; - } - - /** Sets Storage size in bytes before optimization. */ - @NonNull - public Builder setStorageSizeBeforeBytes(long storageSizeBeforeBytes) { - mNativeStorageSizeBeforeBytes = storageSizeBeforeBytes; - return this; - } - - /** Sets storage size in bytes after optimization. */ - @NonNull - public Builder setStorageSizeAfterBytes(long storageSizeAfterBytes) { - mNativeStorageSizeAfterBytes = storageSizeAfterBytes; - return this; - } - - /** - * Sets the amount the time since the last optimize ran calculated using wall clock time. - */ - @NonNull - public Builder setTimeSinceLastOptimizeMillis(long timeSinceLastOptimizeMillis) { - mNativeTimeSinceLastOptimizeMillis = timeSinceLastOptimizeMillis; - return this; - } - - /** Creates a {@link OptimizeStats}. */ - @NonNull - public OptimizeStats build() { - return new OptimizeStats(/* builder= */ this); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java deleted file mode 100644 index 7ba181668bfd5..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/PutDocumentStats.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage.stats; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchResult; - -import java.util.Objects; - -/** - * A class for holding detailed stats to log for each individual document put by a {@link - * android.app.appsearch.AppSearchSession#put} call. - * - * @hide - */ -public final class PutDocumentStats { - @NonNull private final String mPackageName; - @NonNull private final String mDatabase; - /** - * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal - * state. - */ - @AppSearchResult.ResultCode private final int mStatusCode; - - private final int mTotalLatencyMillis; - - /** Time used to generate a document proto from a Bundle. */ - private final int mGenerateDocumentProtoLatencyMillis; - - /** Time used to rewrite types and namespaces in the document. */ - private final int mRewriteDocumentTypesLatencyMillis; - - /** Overall time used for the native function call. */ - private final int mNativeLatencyMillis; - - /** Time used to store the document. */ - private final int mNativeDocumentStoreLatencyMillis; - - /** Time used to index the document. It doesn't include the time to merge indices. */ - private final int mNativeIndexLatencyMillis; - - /** Time used to merge the indices. */ - private final int mNativeIndexMergeLatencyMillis; - - /** Document size in bytes. */ - private final int mNativeDocumentSizeBytes; - - /** Number of tokens added to the index. */ - private final int mNativeNumTokensIndexed; - - /** - * Whether the number of tokens to be indexed exceeded the max number of tokens per document. - */ - private final boolean mNativeExceededMaxNumTokens; - - PutDocumentStats(@NonNull Builder builder) { - Objects.requireNonNull(builder); - mPackageName = builder.mPackageName; - mDatabase = builder.mDatabase; - mStatusCode = builder.mStatusCode; - mTotalLatencyMillis = builder.mTotalLatencyMillis; - mGenerateDocumentProtoLatencyMillis = builder.mGenerateDocumentProtoLatencyMillis; - mRewriteDocumentTypesLatencyMillis = builder.mRewriteDocumentTypesLatencyMillis; - mNativeLatencyMillis = builder.mNativeLatencyMillis; - mNativeDocumentStoreLatencyMillis = builder.mNativeDocumentStoreLatencyMillis; - mNativeIndexLatencyMillis = builder.mNativeIndexLatencyMillis; - mNativeIndexMergeLatencyMillis = builder.mNativeIndexMergeLatencyMillis; - mNativeDocumentSizeBytes = builder.mNativeDocumentSizeBytes; - mNativeNumTokensIndexed = builder.mNativeNumTokensIndexed; - mNativeExceededMaxNumTokens = builder.mNativeExceededMaxNumTokens; - } - - /** Returns calling package name. */ - @NonNull - public String getPackageName() { - return mPackageName; - } - - /** Returns calling database name. */ - @NonNull - public String getDatabase() { - return mDatabase; - } - - /** Returns status code for this putDocument. */ - @AppSearchResult.ResultCode - public int getStatusCode() { - return mStatusCode; - } - - /** Returns total latency of this putDocument in millis. */ - public int getTotalLatencyMillis() { - return mTotalLatencyMillis; - } - - /** Returns time spent on generating document proto, in milliseconds. */ - public int getGenerateDocumentProtoLatencyMillis() { - return mGenerateDocumentProtoLatencyMillis; - } - - /** Returns time spent on rewriting types and namespaces in document, in milliseconds. */ - public int getRewriteDocumentTypesLatencyMillis() { - return mRewriteDocumentTypesLatencyMillis; - } - - /** Returns time spent in native, in milliseconds. */ - public int getNativeLatencyMillis() { - return mNativeLatencyMillis; - } - - /** Returns time spent on document store, in milliseconds. */ - public int getNativeDocumentStoreLatencyMillis() { - return mNativeDocumentStoreLatencyMillis; - } - - /** Returns time spent on indexing, in milliseconds. */ - public int getNativeIndexLatencyMillis() { - return mNativeIndexLatencyMillis; - } - - /** Returns time spent on merging indices, in milliseconds. */ - public int getNativeIndexMergeLatencyMillis() { - return mNativeIndexMergeLatencyMillis; - } - - /** Returns document size, in bytes. */ - public int getNativeDocumentSizeBytes() { - return mNativeDocumentSizeBytes; - } - - /** Returns number of tokens indexed. */ - public int getNativeNumTokensIndexed() { - return mNativeNumTokensIndexed; - } - - /** - * Returns whether the number of tokens to be indexed exceeded the max number of tokens per - * document. - */ - public boolean getNativeExceededMaxNumTokens() { - return mNativeExceededMaxNumTokens; - } - - /** Builder for {@link PutDocumentStats}. */ - public static class Builder { - @NonNull final String mPackageName; - @NonNull final String mDatabase; - @AppSearchResult.ResultCode int mStatusCode; - int mTotalLatencyMillis; - int mGenerateDocumentProtoLatencyMillis; - int mRewriteDocumentTypesLatencyMillis; - int mNativeLatencyMillis; - int mNativeDocumentStoreLatencyMillis; - int mNativeIndexLatencyMillis; - int mNativeIndexMergeLatencyMillis; - int mNativeDocumentSizeBytes; - int mNativeNumTokensIndexed; - boolean mNativeExceededMaxNumTokens; - - /** Builder for {@link PutDocumentStats} */ - public Builder(@NonNull String packageName, @NonNull String database) { - mPackageName = Objects.requireNonNull(packageName); - mDatabase = Objects.requireNonNull(database); - } - - /** Sets the status code. */ - @NonNull - public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) { - mStatusCode = statusCode; - return this; - } - - /** Sets total latency in millis. */ - @NonNull - public Builder setTotalLatencyMillis(int totalLatencyMillis) { - mTotalLatencyMillis = totalLatencyMillis; - return this; - } - - /** Sets how much time we spend for generating document proto, in milliseconds. */ - @NonNull - public Builder setGenerateDocumentProtoLatencyMillis( - int generateDocumentProtoLatencyMillis) { - mGenerateDocumentProtoLatencyMillis = generateDocumentProtoLatencyMillis; - return this; - } - - /** - * Sets how much time we spend for rewriting types and namespaces in document, in - * milliseconds. - */ - @NonNull - public Builder setRewriteDocumentTypesLatencyMillis(int rewriteDocumentTypesLatencyMillis) { - mRewriteDocumentTypesLatencyMillis = rewriteDocumentTypesLatencyMillis; - return this; - } - - /** Sets the native latency, in milliseconds. */ - @NonNull - public Builder setNativeLatencyMillis(int nativeLatencyMillis) { - mNativeLatencyMillis = nativeLatencyMillis; - return this; - } - - /** Sets how much time we spend on document store, in milliseconds. */ - @NonNull - public Builder setNativeDocumentStoreLatencyMillis(int nativeDocumentStoreLatencyMillis) { - mNativeDocumentStoreLatencyMillis = nativeDocumentStoreLatencyMillis; - return this; - } - - /** Sets the native index latency, in milliseconds. */ - @NonNull - public Builder setNativeIndexLatencyMillis(int nativeIndexLatencyMillis) { - mNativeIndexLatencyMillis = nativeIndexLatencyMillis; - return this; - } - - /** Sets how much time we spend on merging indices, in milliseconds. */ - @NonNull - public Builder setNativeIndexMergeLatencyMillis(int nativeIndexMergeLatencyMillis) { - mNativeIndexMergeLatencyMillis = nativeIndexMergeLatencyMillis; - return this; - } - - /** Sets document size, in bytes. */ - @NonNull - public Builder setNativeDocumentSizeBytes(int nativeDocumentSizeBytes) { - mNativeDocumentSizeBytes = nativeDocumentSizeBytes; - return this; - } - - /** Sets number of tokens indexed in native. */ - @NonNull - public Builder setNativeNumTokensIndexed(int nativeNumTokensIndexed) { - mNativeNumTokensIndexed = nativeNumTokensIndexed; - return this; - } - - /** - * Sets whether the number of tokens to be indexed exceeded the max number of tokens per - * document. - */ - @NonNull - public Builder setNativeExceededMaxNumTokens(boolean nativeExceededMaxNumTokens) { - mNativeExceededMaxNumTokens = nativeExceededMaxNumTokens; - return this; - } - - /** - * Creates a new {@link PutDocumentStats} object from the contents of this {@link Builder} - * instance. - */ - @NonNull - public PutDocumentStats build() { - return new PutDocumentStats(/* builder= */ this); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/RemoveStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/RemoveStats.java deleted file mode 100644 index b900c49e7937b..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/RemoveStats.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage.stats; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.app.appsearch.AppSearchResult; -import android.app.appsearch.RemoveByDocumentIdRequest; -import android.app.appsearch.SearchSpec; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * Class holds detailed stats for {@link - * android.app.appsearch.AppSearchSession#remove(RemoveByDocumentIdRequest)} and {@link - * android.app.appsearch.AppSearchSession#remove(String, SearchSpec)} - * - * @hide - */ -public final class RemoveStats { - @IntDef( - value = { - // It needs to be sync with DeleteType.Code in - // external/icing/proto/icing/proto/logging.proto#DeleteStatsProto - UNKNOWN, - SINGLE, - QUERY, - NAMESPACE, - SCHEMA_TYPE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface DeleteType {} - - /** Default. Should never be used. */ - public static final int UNKNOWN = 0; - /** Delete by namespace + id. */ - public static final int SINGLE = 1; - /** Delete by query. */ - public static final int QUERY = 2; - /** Delete by namespace. */ - public static final int NAMESPACE = 3; - /** Delete by schema type. */ - public static final int SCHEMA_TYPE = 4; - - @NonNull private final String mPackageName; - @NonNull private final String mDatabase; - /** - * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal - * state. - */ - @AppSearchResult.ResultCode private final int mStatusCode; - - private final int mTotalLatencyMillis; - private final int mNativeLatencyMillis; - @DeleteType private final int mNativeDeleteType; - private final int mNativeNumDocumentsDeleted; - - RemoveStats(@NonNull Builder builder) { - Objects.requireNonNull(builder); - mPackageName = builder.mPackageName; - mDatabase = builder.mDatabase; - mStatusCode = builder.mStatusCode; - mTotalLatencyMillis = builder.mTotalLatencyMillis; - mNativeLatencyMillis = builder.mNativeLatencyMillis; - mNativeDeleteType = builder.mNativeDeleteType; - mNativeNumDocumentsDeleted = builder.mNativeNumDocumentsDeleted; - } - - /** Returns calling package name. */ - @NonNull - public String getPackageName() { - return mPackageName; - } - - /** Returns calling database name. */ - @NonNull - public String getDatabase() { - return mDatabase; - } - - /** Returns status code for this remove. */ - @AppSearchResult.ResultCode - public int getStatusCode() { - return mStatusCode; - } - - /** Returns total latency of this remove in millis. */ - public int getTotalLatencyMillis() { - return mTotalLatencyMillis; - } - - /** Returns how much time in millis spent in the native code. */ - public int getNativeLatencyMillis() { - return mNativeLatencyMillis; - } - - /** Returns what type of delete for this remove call. */ - @DeleteType - public int getDeleteType() { - return mNativeDeleteType; - } - - /** Returns how many documents get deleted in this call. */ - public int getDeletedDocumentCount() { - return mNativeNumDocumentsDeleted; - } - - /** Builder for {@link RemoveStats}. */ - public static class Builder { - @NonNull final String mPackageName; - @NonNull final String mDatabase; - @AppSearchResult.ResultCode int mStatusCode; - int mTotalLatencyMillis; - int mNativeLatencyMillis; - @DeleteType int mNativeDeleteType; - int mNativeNumDocumentsDeleted; - - /** Constructor for the {@link Builder}. */ - public Builder(@NonNull String packageName, @NonNull String database) { - mPackageName = Objects.requireNonNull(packageName); - mDatabase = Objects.requireNonNull(database); - } - - /** Sets the status code. */ - @NonNull - public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) { - mStatusCode = statusCode; - return this; - } - - /** Sets total latency in millis. */ - @NonNull - public Builder setTotalLatencyMillis(int totalLatencyMillis) { - mTotalLatencyMillis = totalLatencyMillis; - return this; - } - - /** Sets native latency in millis. */ - @NonNull - public Builder setNativeLatencyMillis(int nativeLatencyMillis) { - mNativeLatencyMillis = nativeLatencyMillis; - return this; - } - - /** Sets delete type for this call. */ - @NonNull - public Builder setDeleteType(@DeleteType int nativeDeleteType) { - mNativeDeleteType = nativeDeleteType; - return this; - } - - /** Sets how many documents get deleted for this call. */ - @NonNull - public Builder setDeletedDocumentCount(int nativeNumDocumentsDeleted) { - mNativeNumDocumentsDeleted = nativeNumDocumentsDeleted; - return this; - } - - /** Creates a {@link RemoveStats}. */ - @NonNull - public RemoveStats build() { - return new RemoveStats(/* builder= */ this); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SchemaMigrationStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SchemaMigrationStats.java deleted file mode 100644 index 6e1e2d5f774b9..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SchemaMigrationStats.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage.stats; - -import android.annotation.NonNull; -import android.app.appsearch.SetSchemaRequest; - -import java.util.Objects; - -/** - * Class holds detailed stats for Schema migration. - * - * @hide - */ -// TODO(b/173532925): Hides getter and setter functions for accessing {@code -// mFirstSetSchemaLatencyMillis} and {@code mSecondSetSchemaLatencyMillis} field. - -public final class SchemaMigrationStats { - /** GetSchema latency in milliseconds. */ - private final int mGetSchemaLatencyMillis; - - /** - * Latency of querying all documents that need to be migrated to new version and transforming - * documents to new version in milliseconds. - */ - private final int mQueryAndTransformLatencyMillis; - - private final int mFirstSetSchemaLatencyMillis; - - private final int mSecondSetSchemaLatencyMillis; - - /** Latency of putting migrated document to Icing lib in milliseconds. */ - private final int mSaveDocumentLatencyMillis; - - private final int mMigratedDocumentCount; - - private final int mSavedDocumentCount; - - SchemaMigrationStats(@NonNull Builder builder) { - Objects.requireNonNull(builder); - mGetSchemaLatencyMillis = builder.mGetSchemaLatencyMillis; - mQueryAndTransformLatencyMillis = builder.mQueryAndTransformLatencyMillis; - mFirstSetSchemaLatencyMillis = builder.mFirstSetSchemaLatencyMillis; - mSecondSetSchemaLatencyMillis = builder.mSecondSetSchemaLatencyMillis; - mSaveDocumentLatencyMillis = builder.mSaveDocumentLatencyMillis; - mMigratedDocumentCount = builder.mMigratedDocumentCount; - mSavedDocumentCount = builder.mSavedDocumentCount; - } - - /** Returns GetSchema latency in milliseconds. */ - public int getGetSchemaLatencyMillis() { - return mGetSchemaLatencyMillis; - } - - /** - * Returns latency of querying all documents that need to be migrated to new version and - * transforming documents to new version in milliseconds. - */ - public int getQueryAndTransformLatencyMillis() { - return mQueryAndTransformLatencyMillis; - } - - /** - * Returns latency of first SetSchema action in milliseconds. - * - *

    If all schema fields are backward compatible, the schema will be successful set to Icing. - * Otherwise, we will retrieve incompatible types here. - * - *

    Please see {@link SetSchemaRequest} for what is "incompatible". - */ - public int getFirstSetSchemaLatencyMillis() { - return mFirstSetSchemaLatencyMillis; - } - - /** - * Returns latency of second SetSchema action in milliseconds. - * - *

    If all schema fields are backward compatible, the schema will be successful set to Icing - * in the first setSchema action and this value will be 0. Otherwise, schema types will be set - * to Icing by this action. - */ - public int getSecondSetSchemaLatencyMillis() { - return mSecondSetSchemaLatencyMillis; - } - - /** Returns latency of putting migrated document to Icing lib in milliseconds. */ - public int getSaveDocumentLatencyMillis() { - return mSaveDocumentLatencyMillis; - } - - /** Returns number of migrated documents. */ - public int getMigratedDocumentCount() { - return mMigratedDocumentCount; - } - - /** Returns number of updated documents which are saved in Icing lib. */ - public int getSavedDocumentCount() { - return mSavedDocumentCount; - } - - /** Builder for {@link SchemaMigrationStats}. */ - public static class Builder { - int mGetSchemaLatencyMillis; - int mQueryAndTransformLatencyMillis; - int mFirstSetSchemaLatencyMillis; - int mSecondSetSchemaLatencyMillis; - int mSaveDocumentLatencyMillis; - int mMigratedDocumentCount; - int mSavedDocumentCount; - - /** Sets latency for the GetSchema action in milliseconds. */ - @NonNull - public SchemaMigrationStats.Builder setGetSchemaLatencyMillis(int getSchemaLatencyMillis) { - mGetSchemaLatencyMillis = getSchemaLatencyMillis; - return this; - } - - /** - * Sets latency for querying all documents that need to be migrated to new version and - * transforming documents to new version in milliseconds. - */ - @NonNull - public SchemaMigrationStats.Builder setQueryAndTransformLatencyMillis( - int queryAndTransformLatencyMillis) { - mQueryAndTransformLatencyMillis = queryAndTransformLatencyMillis; - return this; - } - - /** Sets latency of first SetSchema action in milliseconds. */ - @NonNull - public SchemaMigrationStats.Builder setFirstSetSchemaLatencyMillis( - int firstSetSchemaLatencyMillis) { - mFirstSetSchemaLatencyMillis = firstSetSchemaLatencyMillis; - return this; - } - - /** Sets latency of second SetSchema action in milliseconds. */ - @NonNull - public SchemaMigrationStats.Builder setSecondSetSchemaLatencyMillis( - int secondSetSchemaLatencyMillis) { - mSecondSetSchemaLatencyMillis = secondSetSchemaLatencyMillis; - return this; - } - - /** Sets latency for putting migrated document to Icing lib in milliseconds. */ - @NonNull - public SchemaMigrationStats.Builder setSaveDocumentLatencyMillis( - int saveDocumentLatencyMillis) { - mSaveDocumentLatencyMillis = saveDocumentLatencyMillis; - return this; - } - - /** Sets number of migrated documents. */ - @NonNull - public SchemaMigrationStats.Builder setMigratedDocumentCount(int migratedDocumentCount) { - mMigratedDocumentCount = migratedDocumentCount; - return this; - } - - /** Sets number of updated documents which are saved in Icing lib. */ - @NonNull - public SchemaMigrationStats.Builder setSavedDocumentCount(int savedDocumentCount) { - mSavedDocumentCount = savedDocumentCount; - return this; - } - - /** - * Builds a new {@link SchemaMigrationStats} from the {@link SchemaMigrationStats.Builder}. - */ - @NonNull - public SchemaMigrationStats build() { - return new SchemaMigrationStats(/* builder= */ this); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java deleted file mode 100644 index d7904f3ca49fc..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.server.appsearch.external.localstorage.stats; - -import android.annotation.IntDef; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.AppSearchResult; -import android.app.appsearch.SearchSpec; - -import com.android.internal.util.Preconditions; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -/** - * Class holds detailed stats for {@link android.app.appsearch.AppSearchSession#search(String, - * SearchSpec)} - * - * @hide - */ -public final class SearchStats { - @IntDef( - value = { - // Searches apps' own documents. - VISIBILITY_SCOPE_LOCAL, - // Searches the global documents. Including platform surfaceable and 3p-access. - VISIBILITY_SCOPE_GLOBAL, - // TODO(b/173532925) Add THIRD_PARTY_ACCESS once we can distinguish platform - // surfaceable from 3p access(right both of them are categorized as - // VISIBILITY_SCOPE_GLOBAL) - }) - @Retention(RetentionPolicy.SOURCE) - public @interface VisibilityScope {} - - // Searches apps' own documents. - public static final int VISIBILITY_SCOPE_LOCAL = 1; - // Searches the global documents. Including platform surfaceable and 3p-access. - public static final int VISIBILITY_SCOPE_GLOBAL = 2; - - // TODO(b/173532925): Add a field searchType to indicate where the search is used(normal - // query vs in removeByQuery vs during migration) - - @NonNull private final String mPackageName; - @Nullable private final String mDatabase; - /** - * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal - * state. - */ - @AppSearchResult.ResultCode private final int mStatusCode; - - private final int mTotalLatencyMillis; - /** Time used to rewrite the search spec. */ - private final int mRewriteSearchSpecLatencyMillis; - /** Time used to rewrite the search results. */ - private final int mRewriteSearchResultLatencyMillis; - /** Defines the scope the query is searching over */ - @VisibilityScope private final int mVisibilityScope; - /** Overall time used for the native function call. */ - private final int mNativeLatencyMillis; - /** Number of terms in the query string. */ - private final int mNativeNumTerms; - /** Length of the query string. */ - private final int mNativeQueryLength; - /** Number of namespaces filtered. */ - private final int mNativeNumNamespacesFiltered; - /** Number of schema types filtered. */ - private final int mNativeNumSchemaTypesFiltered; - /** The requested number of results in one page. */ - private final int mNativeRequestedPageSize; - /** The actual number of results returned in the current page. */ - private final int mNativeNumResultsReturnedCurrentPage; - /** - * Whether the function call is querying the first page. If it's not, Icing will fetch the - * results from cache so that some steps may be skipped. - */ - private final boolean mNativeIsFirstPage; - /** - * Time used to parse the query, including 2 parts: tokenizing and transforming tokens into an - * iterator tree. - */ - private final int mNativeParseQueryLatencyMillis; - /** Strategy of scoring and ranking. */ - @SearchSpec.RankingStrategy private final int mNativeRankingStrategy; - /** Number of documents scored. */ - private final int mNativeNumDocumentsScored; - /** Time used to score the raw results. */ - private final int mNativeScoringLatencyMillis; - /** Time used to rank the scored results. */ - private final int mNativeRankingLatencyMillis; - /** - * Time used to fetch the document protos. Note that it includes the time to snippet if {@link - * SearchStats#mNativeNumResultsWithSnippets} is greater than 0. - */ - private final int mNativeDocumentRetrievingLatencyMillis; - /** How many snippets are calculated. */ - private final int mNativeNumResultsWithSnippets; - - SearchStats(@NonNull Builder builder) { - Objects.requireNonNull(builder); - mPackageName = builder.mPackageName; - mDatabase = builder.mDatabase; - mStatusCode = builder.mStatusCode; - mTotalLatencyMillis = builder.mTotalLatencyMillis; - mRewriteSearchSpecLatencyMillis = builder.mRewriteSearchSpecLatencyMillis; - mRewriteSearchResultLatencyMillis = builder.mRewriteSearchResultLatencyMillis; - mVisibilityScope = builder.mVisibilityScope; - mNativeLatencyMillis = builder.mNativeLatencyMillis; - mNativeNumTerms = builder.mNativeNumTerms; - mNativeQueryLength = builder.mNativeQueryLength; - mNativeNumNamespacesFiltered = builder.mNativeNumNamespacesFiltered; - mNativeNumSchemaTypesFiltered = builder.mNativeNumSchemaTypesFiltered; - mNativeRequestedPageSize = builder.mNativeRequestedPageSize; - mNativeNumResultsReturnedCurrentPage = builder.mNativeNumResultsReturnedCurrentPage; - mNativeIsFirstPage = builder.mNativeIsFirstPage; - mNativeParseQueryLatencyMillis = builder.mNativeParseQueryLatencyMillis; - mNativeRankingStrategy = builder.mNativeRankingStrategy; - mNativeNumDocumentsScored = builder.mNativeNumDocumentsScored; - mNativeScoringLatencyMillis = builder.mNativeScoringLatencyMillis; - mNativeRankingLatencyMillis = builder.mNativeRankingLatencyMillis; - mNativeNumResultsWithSnippets = builder.mNativeNumResultsWithSnippets; - mNativeDocumentRetrievingLatencyMillis = builder.mNativeDocumentRetrievingLatencyMillis; - } - - /** Returns the package name of the session. */ - @NonNull - public String getPackageName() { - return mPackageName; - } - - /** - * Returns the database name of the session. - * - * @return database name used by the session. {@code null} if and only if it is a global - * search(visibilityScope is {@link SearchStats#VISIBILITY_SCOPE_GLOBAL}). - */ - @Nullable - public String getDatabase() { - return mDatabase; - } - - /** Returns status of the search. */ - @AppSearchResult.ResultCode - public int getStatusCode() { - return mStatusCode; - } - - /** Returns the total latency of the search. */ - public int getTotalLatencyMillis() { - return mTotalLatencyMillis; - } - - /** Returns how much time spent on rewriting the {@link SearchSpec}. */ - public int getRewriteSearchSpecLatencyMillis() { - return mRewriteSearchSpecLatencyMillis; - } - - /** Returns how much time spent on rewriting the {@link android.app.appsearch.SearchResult}. */ - public int getRewriteSearchResultLatencyMillis() { - return mRewriteSearchResultLatencyMillis; - } - - /** Returns the visibility scope of the search. */ - @VisibilityScope - public int getVisibilityScope() { - return mVisibilityScope; - } - - /** Returns how much time spent on the native calls. */ - public int getNativeLatencyMillis() { - return mNativeLatencyMillis; - } - - /** Returns number of terms in the search string. */ - public int getTermCount() { - return mNativeNumTerms; - } - - /** Returns the length of the search string. */ - public int getQueryLength() { - return mNativeQueryLength; - } - - /** Returns number of namespaces filtered. */ - public int getFilteredNamespaceCount() { - return mNativeNumNamespacesFiltered; - } - - /** Returns number of schema types filtered. */ - public int getFilteredSchemaTypeCount() { - return mNativeNumSchemaTypesFiltered; - } - - /** Returns the requested number of results in one page. */ - public int getRequestedPageSize() { - return mNativeRequestedPageSize; - } - - /** Returns the actual number of results returned in the current page. */ - public int getCurrentPageReturnedResultCount() { - return mNativeNumResultsReturnedCurrentPage; - } - - // TODO(b/185184738) Make it an integer to show how many pages having been returned. - /** Returns whether the function call is querying the first page. */ - public boolean isFirstPage() { - return mNativeIsFirstPage; - } - - /** - * Returns time used to parse the query, including 2 parts: tokenizing and transforming tokens - * into an iterator tree. - */ - public int getParseQueryLatencyMillis() { - return mNativeParseQueryLatencyMillis; - } - - /** Returns strategy of scoring and ranking. */ - @SearchSpec.RankingStrategy - public int getRankingStrategy() { - return mNativeRankingStrategy; - } - - /** Returns number of documents scored. */ - public int getScoredDocumentCount() { - return mNativeNumDocumentsScored; - } - - /** Returns time used to score the raw results. */ - public int getScoringLatencyMillis() { - return mNativeScoringLatencyMillis; - } - - /** Returns time used to rank the scored results. */ - public int getRankingLatencyMillis() { - return mNativeRankingLatencyMillis; - } - - /** - * Returns time used to fetch the document protos. Note that it includes the time to snippet if - * {@link SearchStats#mNativeNumResultsWithSnippets} is not zero. - */ - public int getDocumentRetrievingLatencyMillis() { - return mNativeDocumentRetrievingLatencyMillis; - } - - /** Returns the number of the results in the page returned were snippeted. */ - public int getResultWithSnippetsCount() { - return mNativeNumResultsWithSnippets; - } - - /** Builder for {@link SearchStats} */ - public static class Builder { - @NonNull final String mPackageName; - @Nullable String mDatabase; - @AppSearchResult.ResultCode int mStatusCode; - int mTotalLatencyMillis; - int mRewriteSearchSpecLatencyMillis; - int mRewriteSearchResultLatencyMillis; - int mVisibilityScope; - int mNativeLatencyMillis; - int mNativeNumTerms; - int mNativeQueryLength; - int mNativeNumNamespacesFiltered; - int mNativeNumSchemaTypesFiltered; - int mNativeRequestedPageSize; - int mNativeNumResultsReturnedCurrentPage; - boolean mNativeIsFirstPage; - int mNativeParseQueryLatencyMillis; - int mNativeRankingStrategy; - int mNativeNumDocumentsScored; - int mNativeScoringLatencyMillis; - int mNativeRankingLatencyMillis; - int mNativeNumResultsWithSnippets; - int mNativeDocumentRetrievingLatencyMillis; - - /** - * Constructor - * - * @param visibilityScope scope for the corresponding search. - * @param packageName name of the calling package. - */ - public Builder(@VisibilityScope int visibilityScope, @NonNull String packageName) { - mVisibilityScope = visibilityScope; - mPackageName = Objects.requireNonNull(packageName); - } - - /** Sets the database used by the session. */ - @NonNull - public Builder setDatabase(@NonNull String database) { - mDatabase = Objects.requireNonNull(database); - return this; - } - - /** Sets the status of the search. */ - @NonNull - public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) { - mStatusCode = statusCode; - return this; - } - - /** Sets total latency for the search. */ - @NonNull - public Builder setTotalLatencyMillis(int totalLatencyMillis) { - mTotalLatencyMillis = totalLatencyMillis; - return this; - } - - /** Sets time used to rewrite the search spec. */ - @NonNull - public Builder setRewriteSearchSpecLatencyMillis(int rewriteSearchSpecLatencyMillis) { - mRewriteSearchSpecLatencyMillis = rewriteSearchSpecLatencyMillis; - return this; - } - - /** Sets time used to rewrite the search results. */ - @NonNull - public Builder setRewriteSearchResultLatencyMillis(int rewriteSearchResultLatencyMillis) { - mRewriteSearchResultLatencyMillis = rewriteSearchResultLatencyMillis; - return this; - } - - /** Sets overall time used for the native function calls. */ - @NonNull - public Builder setNativeLatencyMillis(int nativeLatencyMillis) { - mNativeLatencyMillis = nativeLatencyMillis; - return this; - } - - /** Sets number of terms in the search string. */ - @NonNull - public Builder setTermCount(int termCount) { - mNativeNumTerms = termCount; - return this; - } - - /** Sets length of the search string. */ - @NonNull - public Builder setQueryLength(int queryLength) { - mNativeQueryLength = queryLength; - return this; - } - - /** Sets number of namespaces filtered. */ - @NonNull - public Builder setFilteredNamespaceCount(int filteredNamespaceCount) { - mNativeNumNamespacesFiltered = filteredNamespaceCount; - return this; - } - - /** Sets number of schema types filtered. */ - @NonNull - public Builder setFilteredSchemaTypeCount(int filteredSchemaTypeCount) { - mNativeNumSchemaTypesFiltered = filteredSchemaTypeCount; - return this; - } - - /** Sets the requested number of results in one page. */ - @NonNull - public Builder setRequestedPageSize(int requestedPageSize) { - mNativeRequestedPageSize = requestedPageSize; - return this; - } - - /** Sets the actual number of results returned in the current page. */ - @NonNull - public Builder setCurrentPageReturnedResultCount(int currentPageReturnedResultCount) { - mNativeNumResultsReturnedCurrentPage = currentPageReturnedResultCount; - return this; - } - - /** - * Sets whether the function call is querying the first page. If it's not, Icing will fetch - * the results from cache so that some steps may be skipped. - */ - @NonNull - public Builder setIsFirstPage(boolean nativeIsFirstPage) { - mNativeIsFirstPage = nativeIsFirstPage; - return this; - } - - /** - * Sets time used to parse the query, including 2 parts: tokenizing and transforming tokens - * into an iterator tree. - */ - @NonNull - public Builder setParseQueryLatencyMillis(int parseQueryLatencyMillis) { - mNativeParseQueryLatencyMillis = parseQueryLatencyMillis; - return this; - } - - /** Sets strategy of scoring and ranking. */ - @NonNull - public Builder setRankingStrategy(@SearchSpec.RankingStrategy int rankingStrategy) { - mNativeRankingStrategy = rankingStrategy; - return this; - } - - /** Sets number of documents scored. */ - @NonNull - public Builder setScoredDocumentCount(int scoredDocumentCount) { - mNativeNumDocumentsScored = scoredDocumentCount; - return this; - } - - /** Sets time used to score the raw results. */ - @NonNull - public Builder setScoringLatencyMillis(int scoringLatencyMillis) { - mNativeScoringLatencyMillis = scoringLatencyMillis; - return this; - } - - /** Sets time used to rank the scored results. */ - @NonNull - public Builder setRankingLatencyMillis(int rankingLatencyMillis) { - mNativeRankingLatencyMillis = rankingLatencyMillis; - return this; - } - - /** Sets time used to fetch the document protos. */ - @NonNull - public Builder setDocumentRetrievingLatencyMillis(int documentRetrievingLatencyMillis) { - mNativeDocumentRetrievingLatencyMillis = documentRetrievingLatencyMillis; - return this; - } - - /** Sets how many snippets are calculated. */ - @NonNull - public Builder setResultWithSnippetsCount(int resultWithSnippetsCount) { - mNativeNumResultsWithSnippets = resultWithSnippetsCount; - return this; - } - - /** - * Constructs a new {@link SearchStats} from the contents of this {@link - * SearchStats.Builder}. - */ - @NonNull - public SearchStats build() { - if (mDatabase == null) { - Preconditions.checkState( - mVisibilityScope != SearchStats.VISIBILITY_SCOPE_LOCAL, - "database can not be null if visibilityScope is local."); - } - - return new SearchStats(/* builder= */ this); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java deleted file mode 100644 index 9d789a894855b..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage.stats; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.AppSearchResult; - -import java.util.Objects; - -/** - * Class holds detailed stats for {@link - * android.app.appsearch.AppSearchSession#setSchema(SetSchemaRequest)}. - * - * @hide - */ -public final class SetSchemaStats { - @NonNull private final String mPackageName; - - @NonNull private final String mDatabase; - - /** - * The status code returned by {@link AppSearchResult#getResultCode()} for the call or internal - * state. - */ - @AppSearchResult.ResultCode private final int mStatusCode; - - /** - * Stores stats of SchemaMigration in SetSchema process. Is {@code null} if no schema migration - * is needed. - */ - @Nullable private final SchemaMigrationStats mSchemaMigrationStats; - - private final int mTotalLatencyMillis; - - /** Overall time used for the native function call. */ - private final int mNativeLatencyMillis; - - /** Number of newly added schema types. */ - private final int mNewTypeCount; - - /** Number of deleted schema types. */ - private final int mDeletedTypeCount; - - /** Number of compatible schema type changes. */ - private final int mCompatibleTypeChangeCount; - - /** Number of index-incompatible schema type changes. */ - private final int mIndexIncompatibleTypeChangeCount; - - /** Number of backwards-incompatible schema type changes. */ - private final int mBackwardsIncompatibleTypeChangeCount; - - SetSchemaStats(@NonNull Builder builder) { - Objects.requireNonNull(builder); - mPackageName = builder.mPackageName; - mDatabase = builder.mDatabase; - mStatusCode = builder.mStatusCode; - mSchemaMigrationStats = builder.mSchemaMigrationStats; - mTotalLatencyMillis = builder.mTotalLatencyMillis; - mNativeLatencyMillis = builder.mNativeLatencyMillis; - mNewTypeCount = builder.mNewTypeCount; - mDeletedTypeCount = builder.mDeletedTypeCount; - mCompatibleTypeChangeCount = builder.mCompatibleTypeChangeCount; - mIndexIncompatibleTypeChangeCount = builder.mIndexIncompatibleTypeChangeCount; - mBackwardsIncompatibleTypeChangeCount = builder.mBackwardsIncompatibleTypeChangeCount; - } - - /** Returns calling package name. */ - @NonNull - public String getPackageName() { - return mPackageName; - } - - /** Returns calling database name. */ - @NonNull - public String getDatabase() { - return mDatabase; - } - - /** Returns status of the SetSchema action. */ - @AppSearchResult.ResultCode - public int getStatusCode() { - return mStatusCode; - } - - /** - * Returns the status of schema migration, if migration is executed during the SetSchema - * process. Otherwise, returns {@code null}. - */ - @Nullable - public SchemaMigrationStats getSchemaMigrationStats() { - return mSchemaMigrationStats; - } - - /** Returns the total latency of the SetSchema action. */ - public int getTotalLatencyMillis() { - return mTotalLatencyMillis; - } - - /** Returns overall time used for the native function call. */ - public int getNativeLatencyMillis() { - return mNativeLatencyMillis; - } - - /** Returns number of newly added schema types. */ - public int getNewTypeCount() { - return mNewTypeCount; - } - - /** Returns number of deleted schema types. */ - public int getDeletedTypeCount() { - return mDeletedTypeCount; - } - - /** Returns number of compatible type changes. */ - public int getCompatibleTypeChangeCount() { - return mCompatibleTypeChangeCount; - } - - /** - * Returns number of index-incompatible type change. - * - *

    An index-incompatible type change is one that affects how pre-existing data should be - * searched over, such as modifying the {@code IndexingType} of an existing property. - */ - public int getIndexIncompatibleTypeChangeCount() { - return mIndexIncompatibleTypeChangeCount; - } - - /** - * Returns number of backwards-incompatible type change. - * - *

    For details on what constitutes a backward-incompatible type change, please see {@link - * android.app.appsearch.SetSchemaRequest}. - */ - public int getBackwardsIncompatibleTypeChangeCount() { - return mBackwardsIncompatibleTypeChangeCount; - } - - /** Builder for {@link SetSchemaStats}. */ - public static class Builder { - @NonNull final String mPackageName; - @NonNull final String mDatabase; - @AppSearchResult.ResultCode int mStatusCode; - @Nullable SchemaMigrationStats mSchemaMigrationStats; - int mTotalLatencyMillis; - int mNativeLatencyMillis; - int mNewTypeCount; - int mDeletedTypeCount; - int mCompatibleTypeChangeCount; - int mIndexIncompatibleTypeChangeCount; - int mBackwardsIncompatibleTypeChangeCount; - - /** Constructor for the {@link Builder}. */ - public Builder(@NonNull String packageName, @NonNull String database) { - mPackageName = Objects.requireNonNull(packageName); - mDatabase = Objects.requireNonNull(database); - } - - /** Sets the status of the SetSchema action. */ - @NonNull - public Builder setStatusCode(@AppSearchResult.ResultCode int statusCode) { - mStatusCode = statusCode; - return this; - } - - /** Sets the status of schema migration. */ - @NonNull - public Builder setSchemaMigrationStats(@NonNull SchemaMigrationStats schemaMigrationStats) { - mSchemaMigrationStats = Objects.requireNonNull(schemaMigrationStats); - return this; - } - - /** Sets total latency for the SetSchema action in milliseconds. */ - @NonNull - public Builder setTotalLatencyMillis(int totalLatencyMillis) { - mTotalLatencyMillis = totalLatencyMillis; - return this; - } - - /** Sets native latency in milliseconds. */ - @NonNull - public Builder setNativeLatencyMillis(int nativeLatencyMillis) { - mNativeLatencyMillis = nativeLatencyMillis; - return this; - } - - /** Sets number of new types. */ - @NonNull - public Builder setNewTypeCount(int newTypeCount) { - mNewTypeCount = newTypeCount; - return this; - } - - /** Sets number of deleted types. */ - @NonNull - public Builder setDeletedTypeCount(int deletedTypeCount) { - mDeletedTypeCount = deletedTypeCount; - return this; - } - - /** Sets number of compatible type changes. */ - @NonNull - public Builder setCompatibleTypeChangeCount(int compatibleTypeChangeCount) { - mCompatibleTypeChangeCount = compatibleTypeChangeCount; - return this; - } - - /** Sets number of index-incompatible type changes. */ - @NonNull - public Builder setIndexIncompatibleTypeChangeCount(int indexIncompatibleTypeChangeCount) { - mIndexIncompatibleTypeChangeCount = indexIncompatibleTypeChangeCount; - return this; - } - - /** Sets number of backwards-incompatible type changes. */ - @NonNull - public Builder setBackwardsIncompatibleTypeChangeCount( - int backwardsIncompatibleTypeChangeCount) { - mBackwardsIncompatibleTypeChangeCount = backwardsIncompatibleTypeChangeCount; - return this; - } - - /** Builds a new {@link SetSchemaStats} from the {@link Builder}. */ - @NonNull - public SetSchemaStats build() { - return new SetSchemaStats(/* builder= */ this); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java deleted file mode 100644 index 80cfe89ffc1c6..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/util/PrefixUtil.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage.util; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchResult; -import android.app.appsearch.exceptions.AppSearchException; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import com.google.android.icing.proto.DocumentProto; -import com.google.android.icing.proto.PropertyProto; - -/** - * Provides utility functions for working with package + database prefixes. - * - * @hide - */ -public class PrefixUtil { - private static final String TAG = "AppSearchPrefixUtil"; - - @VisibleForTesting public static final char DATABASE_DELIMITER = '/'; - - @VisibleForTesting public static final char PACKAGE_DELIMITER = '$'; - - private PrefixUtil() {} - - /** Creates prefix string for given package name and database name. */ - @NonNull - public static String createPrefix(@NonNull String packageName, @NonNull String databaseName) { - return packageName + PACKAGE_DELIMITER + databaseName + DATABASE_DELIMITER; - } - /** Creates prefix string for given package name. */ - @NonNull - public static String createPackagePrefix(@NonNull String packageName) { - return packageName + PACKAGE_DELIMITER; - } - - /** - * Returns the package name that's contained within the {@code prefix}. - * - * @param prefix Prefix string that contains the package name inside of it. The package name - * must be in the front of the string, and separated from the rest of the string by the - * {@link #PACKAGE_DELIMITER}. - * @return Valid package name. - */ - @NonNull - public static String getPackageName(@NonNull String prefix) { - int delimiterIndex = prefix.indexOf(PACKAGE_DELIMITER); - if (delimiterIndex == -1) { - // This should never happen if we construct our prefixes properly - Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix); - return ""; - } - return prefix.substring(0, delimiterIndex); - } - - /** - * Returns the database name that's contained within the {@code prefix}. - * - * @param prefix Prefix string that contains the database name inside of it. The database name - * must be between the {@link #PACKAGE_DELIMITER} and {@link #DATABASE_DELIMITER} - * @return Valid database name. - */ - @NonNull - public static String getDatabaseName(@NonNull String prefix) { - // TODO (b/184050178) Start database delimiter index search from after package delimiter - int packageDelimiterIndex = prefix.indexOf(PACKAGE_DELIMITER); - int databaseDelimiterIndex = prefix.indexOf(DATABASE_DELIMITER); - if (packageDelimiterIndex == -1) { - // This should never happen if we construct our prefixes properly - Log.wtf(TAG, "Malformed prefix doesn't contain package delimiter: " + prefix); - return ""; - } - if (databaseDelimiterIndex == -1) { - // This should never happen if we construct our prefixes properly - Log.wtf(TAG, "Malformed prefix doesn't contain database delimiter: " + prefix); - return ""; - } - return prefix.substring(packageDelimiterIndex + 1, databaseDelimiterIndex); - } - - /** - * Creates a string with the package and database prefix removed from the input string. - * - * @param prefixedString a string containing a package and database prefix. - * @return a string with the package and database prefix removed. - * @throws AppSearchException if the prefixed value does not contain a valid database name. - */ - @NonNull - public static String removePrefix(@NonNull String prefixedString) throws AppSearchException { - // The prefix is made up of the package, then the database. So we only need to find the - // database cutoff. - int delimiterIndex; - if ((delimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER)) != -1) { - // Add 1 to include the char size of the DATABASE_DELIMITER - return prefixedString.substring(delimiterIndex + 1); - } - throw new AppSearchException( - AppSearchResult.RESULT_INTERNAL_ERROR, - "The prefixed value \"" - + prefixedString - + "\" doesn't contain a valid " - + "database name"); - } - - /** - * Creates a package and database prefix string from the input string. - * - * @param prefixedString a string containing a package and database prefix. - * @return a string with the package and database prefix - * @throws AppSearchException if the prefixed value does not contain a valid database name. - */ - @NonNull - public static String getPrefix(@NonNull String prefixedString) throws AppSearchException { - int databaseDelimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER); - if (databaseDelimiterIndex == -1) { - throw new AppSearchException( - AppSearchResult.RESULT_INTERNAL_ERROR, - "The prefixed value \"" - + prefixedString - + "\" doesn't contain a valid " - + "database name"); - } - - // Add 1 to include the char size of the DATABASE_DELIMITER - return prefixedString.substring(0, databaseDelimiterIndex + 1); - } - - /** - * Prepends {@code prefix} to all types and namespaces mentioned anywhere in {@code - * documentBuilder}. - * - * @param documentBuilder The document to mutate - * @param prefix The prefix to add - */ - public static void addPrefixToDocument( - @NonNull DocumentProto.Builder documentBuilder, @NonNull String prefix) { - // Rewrite the type name to include/remove the prefix. - String newSchema = prefix + documentBuilder.getSchema(); - documentBuilder.setSchema(newSchema); - - // Rewrite the namespace to include/remove the prefix. - documentBuilder.setNamespace(prefix + documentBuilder.getNamespace()); - - // Recurse into derived documents - for (int propertyIdx = 0; - propertyIdx < documentBuilder.getPropertiesCount(); - propertyIdx++) { - int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount(); - if (documentCount > 0) { - PropertyProto.Builder propertyBuilder = - documentBuilder.getProperties(propertyIdx).toBuilder(); - for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) { - DocumentProto.Builder derivedDocumentBuilder = - propertyBuilder.getDocumentValues(documentIdx).toBuilder(); - addPrefixToDocument(derivedDocumentBuilder, prefix); - propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder); - } - documentBuilder.setProperties(propertyIdx, propertyBuilder); - } - } - } - - /** - * Removes any prefixes from types and namespaces mentioned anywhere in {@code documentBuilder}. - * - * @param documentBuilder The document to mutate - * @return Prefix name that was removed from the document. - * @throws AppSearchException if there are unexpected database prefixing errors. - */ - @NonNull - public static String removePrefixesFromDocument(@NonNull DocumentProto.Builder documentBuilder) - throws AppSearchException { - // Rewrite the type name and namespace to remove the prefix. - String schemaPrefix = getPrefix(documentBuilder.getSchema()); - String namespacePrefix = getPrefix(documentBuilder.getNamespace()); - - if (!schemaPrefix.equals(namespacePrefix)) { - throw new AppSearchException( - AppSearchResult.RESULT_INTERNAL_ERROR, - "Found unexpected" - + " multiple prefix names in document: " - + schemaPrefix - + ", " - + namespacePrefix); - } - - documentBuilder.setSchema(removePrefix(documentBuilder.getSchema())); - documentBuilder.setNamespace(removePrefix(documentBuilder.getNamespace())); - - // Recurse into derived documents - for (int propertyIdx = 0; - propertyIdx < documentBuilder.getPropertiesCount(); - propertyIdx++) { - int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount(); - if (documentCount > 0) { - PropertyProto.Builder propertyBuilder = - documentBuilder.getProperties(propertyIdx).toBuilder(); - for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) { - DocumentProto.Builder derivedDocumentBuilder = - propertyBuilder.getDocumentValues(documentIdx).toBuilder(); - String nestedPrefix = removePrefixesFromDocument(derivedDocumentBuilder); - if (!nestedPrefix.equals(schemaPrefix)) { - throw new AppSearchException( - AppSearchResult.RESULT_INTERNAL_ERROR, - "Found unexpected multiple prefix names in document: " - + schemaPrefix - + ", " - + nestedPrefix); - } - propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder); - } - documentBuilder.setProperties(propertyIdx, propertyBuilder); - } - } - - return schemaPrefix; - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java deleted file mode 100644 index fb89250cb096e..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/visibilitystore/VisibilityStore.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.server.appsearch.external.localstorage.visibilitystore; - -import android.annotation.NonNull; -import android.app.appsearch.PackageIdentifier; -import android.app.appsearch.exceptions.AppSearchException; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * An interface for classes that store and validate document visibility data. - * - * @hide - */ -public interface VisibilityStore { - /** - * These cannot have any of the special characters used by AppSearchImpl (e.g. {@code - * AppSearchImpl#PACKAGE_DELIMITER} or {@code AppSearchImpl#DATABASE_DELIMITER}. - */ - String PACKAGE_NAME = "VS#Pkg"; - - @VisibleForTesting String DATABASE_NAME = "VS#Db"; - - /** - * Sets visibility settings for the given database. Any previous visibility settings will be - * overwritten. - * - * @param packageName Package of app that owns the schemas. - * @param databaseName Database that owns the schemas. - * @param schemasNotDisplayedBySystem Set of prefixed schemas that should be hidden from - * platform surfaces. - * @param schemasVisibleToPackages Map of prefixed schemas to a list of package identifiers that - * have access to the schema. - * @throws AppSearchException on AppSearchImpl error. - */ - void setVisibility( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull Set schemasNotDisplayedBySystem, - @NonNull Map> schemasVisibleToPackages) - throws AppSearchException; - - /** - * Checks whether the given package has access to system-surfaceable schemas. - * - * @param callerUid UID of the app that wants to see the data. - */ - boolean isSchemaSearchableByCaller( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String prefixedSchema, - int callerUid, - boolean callerHasSystemAccess); -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java deleted file mode 100644 index fdf6a008b10c5..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java +++ /dev/null @@ -1,527 +0,0 @@ -/* - * 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.stats; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.exceptions.AppSearchException; -import android.content.Context; -import android.os.Process; -import android.os.SystemClock; -import android.util.ArrayMap; -import android.util.Log; -import android.util.SparseIntArray; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.appsearch.AppSearchConfig; -import com.android.server.appsearch.external.localstorage.AppSearchLogger; -import com.android.server.appsearch.external.localstorage.stats.CallStats; -import com.android.server.appsearch.external.localstorage.stats.InitializeStats; -import com.android.server.appsearch.external.localstorage.stats.OptimizeStats; -import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; -import com.android.server.appsearch.external.localstorage.stats.RemoveStats; -import com.android.server.appsearch.external.localstorage.stats.SearchStats; -import com.android.server.appsearch.util.PackageUtil; - -import java.io.UnsupportedEncodingException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Map; -import java.util.Objects; -import java.util.Random; - -/** - * Logger Implementation for pushed atoms. - * - *

    This class is thread-safe. - * - * @hide - */ -public final class PlatformLogger implements AppSearchLogger { - private static final String TAG = "AppSearchPlatformLogger"; - - // Context of the user we're logging for. - private final Context mUserContext; - - // Manager holding the configuration flags - private final AppSearchConfig mConfig; - - private final Random mRng = new Random(); - private final Object mLock = new Object(); - - /** - * SparseArray to track how many stats we skipped due to - * {@link AppSearchConfig#getCachedMinTimeIntervalBetweenSamplesMillis()}. - * - *

    We can have correct extrapolated number by adding those counts back when we log - * the same type of stats next time. E.g. the true count of an event could be estimated as: - * SUM(sampling_interval * (num_skipped_sample + 1)) as est_count - * - *

    The key to the SparseArray is {@link CallStats.CallType} - */ - @GuardedBy("mLock") - private final SparseIntArray mSkippedSampleCountLocked = - new SparseIntArray(); - - /** - * Map to cache the packageUid for each package. - * - *

    It maps packageName to packageUid. - * - *

    The entry will be removed whenever the app gets uninstalled - */ - @GuardedBy("mLock") - private final Map mPackageUidCacheLocked = - new ArrayMap<>(); - - /** - * Elapsed time for last stats logged from boot in millis - */ - @GuardedBy("mLock") - private long mLastPushTimeMillisLocked = 0; - - /** - * Helper class to hold platform specific stats for statsd. - */ - static final class ExtraStats { - // UID for the calling package of the stats. - final int mPackageUid; - // sampling interval for the call type of the stats. - final int mSamplingInterval; - // number of samplings skipped before the current one for the same call type. - final int mSkippedSampleCount; - - ExtraStats(int packageUid, int samplingInterval, int skippedSampleCount) { - mPackageUid = packageUid; - mSamplingInterval = samplingInterval; - mSkippedSampleCount = skippedSampleCount; - } - } - - /** - * Constructor - */ - public PlatformLogger( - @NonNull Context userContext, - @NonNull AppSearchConfig config) { - mUserContext = Objects.requireNonNull(userContext); - mConfig = Objects.requireNonNull(config); - } - - /** Logs {@link CallStats}. */ - @Override - public void logStats(@NonNull CallStats stats) { - Objects.requireNonNull(stats); - synchronized (mLock) { - if (shouldLogForTypeLocked(stats.getCallType())) { - logStatsImplLocked(stats); - } - } - } - - /** Logs {@link PutDocumentStats}. */ - @Override - public void logStats(@NonNull PutDocumentStats stats) { - Objects.requireNonNull(stats); - synchronized (mLock) { - if (shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)) { - logStatsImplLocked(stats); - } - } - } - - @Override - public void logStats(@NonNull InitializeStats stats) { - Objects.requireNonNull(stats); - synchronized (mLock) { - if (shouldLogForTypeLocked(CallStats.CALL_TYPE_INITIALIZE)) { - logStatsImplLocked(stats); - } - } - } - - @Override - public void logStats(@NonNull SearchStats stats) { - Objects.requireNonNull(stats); - synchronized (mLock) { - if (shouldLogForTypeLocked(CallStats.CALL_TYPE_SEARCH)) { - logStatsImplLocked(stats); - } - } - } - - @Override - public void logStats(@NonNull RemoveStats stats) { - // TODO(b/173532925): Log stats - } - - @Override - public void logStats(@NonNull OptimizeStats stats) { - Objects.requireNonNull(stats); - synchronized (mLock) { - if (shouldLogForTypeLocked(CallStats.CALL_TYPE_OPTIMIZE)) { - logStatsImplLocked(stats); - } - } - } - - /** - * Removes cached UID for package. - * - * @return removed UID for the package, or {@code INVALID_UID} if package was not previously - * cached. - */ - public int removeCachedUidForPackage(@NonNull String packageName) { - // TODO(b/173532925) This needs to be called when we get PACKAGE_REMOVED intent - Objects.requireNonNull(packageName); - synchronized (mLock) { - Integer uid = mPackageUidCacheLocked.remove(packageName); - return uid != null ? uid : Process.INVALID_UID; - } - } - - @GuardedBy("mLock") - private void logStatsImplLocked(@NonNull CallStats stats) { - mLastPushTimeMillisLocked = SystemClock.elapsedRealtime(); - ExtraStats extraStats = createExtraStatsLocked(stats.getPackageName(), stats.getCallType()); - String database = stats.getDatabase(); - try { - int hashCodeForDatabase = calculateHashCodeMd5(database); - AppSearchStatsLog.write(AppSearchStatsLog.APP_SEARCH_CALL_STATS_REPORTED, - extraStats.mSamplingInterval, - extraStats.mSkippedSampleCount, - extraStats.mPackageUid, - hashCodeForDatabase, - stats.getStatusCode(), - stats.getTotalLatencyMillis(), - stats.getCallType(), - stats.getEstimatedBinderLatencyMillis(), - stats.getNumOperationsSucceeded(), - stats.getNumOperationsFailed()); - } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { - // TODO(b/184204720) report hashing error to statsd - // We need to set a special value(e.g. 0xFFFFFFFF) for the hashing of the database, - // so in the dashboard we know there is some error for hashing. - // - // Something is wrong while calculating the hash code for database - // this shouldn't happen since we always use "MD5" and "UTF-8" - if (database != null) { - Log.e(TAG, "Error calculating hash code for database " + database, e); - } - } - } - - @GuardedBy("mLock") - private void logStatsImplLocked(@NonNull PutDocumentStats stats) { - mLastPushTimeMillisLocked = SystemClock.elapsedRealtime(); - ExtraStats extraStats = createExtraStatsLocked( - stats.getPackageName(), CallStats.CALL_TYPE_PUT_DOCUMENT); - String database = stats.getDatabase(); - try { - int hashCodeForDatabase = calculateHashCodeMd5(database); - AppSearchStatsLog.write(AppSearchStatsLog.APP_SEARCH_PUT_DOCUMENT_STATS_REPORTED, - extraStats.mSamplingInterval, - extraStats.mSkippedSampleCount, - extraStats.mPackageUid, - hashCodeForDatabase, - stats.getStatusCode(), - stats.getTotalLatencyMillis(), - stats.getGenerateDocumentProtoLatencyMillis(), - stats.getRewriteDocumentTypesLatencyMillis(), - stats.getNativeLatencyMillis(), - stats.getNativeDocumentStoreLatencyMillis(), - stats.getNativeIndexLatencyMillis(), - stats.getNativeIndexMergeLatencyMillis(), - stats.getNativeDocumentSizeBytes(), - stats.getNativeNumTokensIndexed(), - stats.getNativeExceededMaxNumTokens()); - } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { - // TODO(b/184204720) report hashing error to statsd - // We need to set a special value(e.g. 0xFFFFFFFF) for the hashing of the database, - // so in the dashboard we know there is some error for hashing. - // - // Something is wrong while calculating the hash code for database - // this shouldn't happen since we always use "MD5" and "UTF-8" - if (database != null) { - Log.e(TAG, "Error calculating hash code for database " + database, e); - } - } - } - - @GuardedBy("mLock") - private void logStatsImplLocked(@NonNull SearchStats stats) { - mLastPushTimeMillisLocked = SystemClock.elapsedRealtime(); - ExtraStats extraStats = createExtraStatsLocked(stats.getPackageName(), - CallStats.CALL_TYPE_SEARCH); - String database = stats.getDatabase(); - try { - int hashCodeForDatabase = calculateHashCodeMd5(database); - AppSearchStatsLog.write(AppSearchStatsLog.APP_SEARCH_QUERY_STATS_REPORTED, - extraStats.mSamplingInterval, - extraStats.mSkippedSampleCount, - extraStats.mPackageUid, - hashCodeForDatabase, - stats.getStatusCode(), - stats.getTotalLatencyMillis(), - stats.getRewriteSearchSpecLatencyMillis(), - stats.getRewriteSearchResultLatencyMillis(), - stats.getVisibilityScope(), - stats.getNativeLatencyMillis(), - stats.getTermCount(), - stats.getQueryLength(), - stats.getFilteredNamespaceCount(), - stats.getFilteredSchemaTypeCount(), - stats.getRequestedPageSize(), - stats.getCurrentPageReturnedResultCount(), - stats.isFirstPage(), - stats.getParseQueryLatencyMillis(), - stats.getRankingStrategy(), - stats.getScoredDocumentCount(), - stats.getScoringLatencyMillis(), - stats.getRankingLatencyMillis(), - stats.getDocumentRetrievingLatencyMillis(), - stats.getResultWithSnippetsCount()); - } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { - // TODO(b/184204720) report hashing error to statsd - // We need to set a special value(e.g. 0xFFFFFFFF) for the hashing of the database, - // so in the dashboard we know there is some error for hashing. - // - // Something is wrong while calculating the hash code for database - // this shouldn't happen since we always use "MD5" and "UTF-8" - if (database != null) { - Log.e(TAG, "Error calculating hash code for database " + database, e); - } - } - } - - @GuardedBy("mLock") - private void logStatsImplLocked(@NonNull InitializeStats stats) { - mLastPushTimeMillisLocked = SystemClock.elapsedRealtime(); - ExtraStats extraStats = createExtraStatsLocked(/*packageName=*/ null, - CallStats.CALL_TYPE_INITIALIZE); - AppSearchStatsLog.write(AppSearchStatsLog.APP_SEARCH_INITIALIZE_STATS_REPORTED, - extraStats.mSamplingInterval, - extraStats.mSkippedSampleCount, - extraStats.mPackageUid, - stats.getStatusCode(), - stats.getTotalLatencyMillis(), - stats.hasDeSync(), - stats.getPrepareSchemaAndNamespacesLatencyMillis(), - stats.getPrepareVisibilityStoreLatencyMillis(), - stats.getNativeLatencyMillis(), - stats.getDocumentStoreRecoveryCause(), - stats.getIndexRestorationCause(), - stats.getSchemaStoreRecoveryCause(), - stats.getDocumentStoreRecoveryLatencyMillis(), - stats.getIndexRestorationLatencyMillis(), - stats.getSchemaStoreRecoveryLatencyMillis(), - stats.getDocumentStoreDataStatus(), - stats.getDocumentCount(), - stats.getSchemaTypeCount(), - stats.hasReset(), - stats.getResetStatusCode()); - } - - @GuardedBy("mLock") - private void logStatsImplLocked(@NonNull OptimizeStats stats) { - mLastPushTimeMillisLocked = SystemClock.elapsedRealtime(); - ExtraStats extraStats = createExtraStatsLocked(/*packageName=*/ null, - CallStats.CALL_TYPE_OPTIMIZE); - AppSearchStatsLog.write(AppSearchStatsLog.APP_SEARCH_OPTIMIZE_STATS_REPORTED, - extraStats.mSamplingInterval, - extraStats.mSkippedSampleCount, - stats.getStatusCode(), - stats.getTotalLatencyMillis(), - stats.getNativeLatencyMillis(), - stats.getDocumentStoreOptimizeLatencyMillis(), - stats.getIndexRestorationLatencyMillis(), - stats.getOriginalDocumentCount(), - stats.getDeletedDocumentCount(), - stats.getExpiredDocumentCount(), - stats.getStorageSizeBeforeBytes(), - stats.getStorageSizeAfterBytes(), - stats.getTimeSinceLastOptimizeMillis()); - } - - /** - * Calculate the hash code as an integer by returning the last four bytes of its MD5. - * - * @param str a string - * @return hash code as an integer. returns -1 if str is null. - * @throws AppSearchException if either algorithm or encoding does not exist. - */ - @VisibleForTesting - @NonNull - static int calculateHashCodeMd5(@Nullable String str) throws - NoSuchAlgorithmException, UnsupportedEncodingException { - if (str == null) { - // Just return -1 if caller doesn't have database name - // For some stats like globalQuery, databaseName can be null. - // Since in atom it is an integer, we have to return something here. - return -1; - } - - MessageDigest md = MessageDigest.getInstance("MD5"); - md.update(str.getBytes(/*charsetName=*/ "UTF-8")); - byte[] digest = md.digest(); - - // Since MD5 generates 16 bytes digest, we don't need to check the length here to see - // if it is smaller than sizeof(int)(4). - // - // We generate the same value as BigInteger(digest).intValue(). - // BigInteger takes bytes[] and treat it as big endian. And its intValue() would get the - // lower 4 bytes. So here we take the last 4 bytes and treat them as big endian. - return (digest[12] & 0xFF) << 24 - | (digest[13] & 0xFF) << 16 - | (digest[14] & 0xFF) << 8 - | (digest[15] & 0xFF); - } - - /** - * Creates {@link ExtraStats} to hold additional information generated for logging. - * - *

    This method is called by most of logStatsImplLocked functions to reduce code - * duplication. - */ - // TODO(b/173532925) Once we add CTS test for logging atoms and can inspect the result, we can - // remove this @VisibleForTesting and directly use PlatformLogger.logStats to test sampling and - // rate limiting. - @VisibleForTesting - @GuardedBy("mLock") - @NonNull - ExtraStats createExtraStatsLocked(@Nullable String packageName, - @CallStats.CallType int callType) { - int packageUid = Process.INVALID_UID; - if (packageName != null) { - packageUid = getPackageUidAsUserLocked(packageName); - } - - // The sampling ratio here might be different from the one used in - // shouldLogForTypeLocked if there is a config change in the middle. - // Since it is only one sample, we can just ignore this difference. - // Or we can retrieve samplingRatio at beginning and pass along - // as function parameter, but it will make code less cleaner with some duplication. - int samplingInterval = getSamplingIntervalFromConfig(callType); - int skippedSampleCount = mSkippedSampleCountLocked.get(callType, - /*valueOfKeyIfNotFound=*/ 0); - mSkippedSampleCountLocked.put(callType, 0); - - return new ExtraStats(packageUid, samplingInterval, skippedSampleCount); - } - - /** - * Checks if this stats should be logged. - * - *

    It won't be logged if it is "sampled" out, or it is too close to the previous logged - * stats. - */ - @GuardedBy("mLock") - // TODO(b/173532925) Once we add CTS test for logging atoms and can inspect the result, we can - // remove this @VisibleForTesting and directly use PlatformLogger.logStats to test sampling and - // rate limiting. - @VisibleForTesting - boolean shouldLogForTypeLocked(@CallStats.CallType int callType) { - int samplingInterval = getSamplingIntervalFromConfig(callType); - // Sampling - if (!shouldSample(samplingInterval)) { - return false; - } - - // Rate limiting - // Check the timestamp to see if it is too close to last logged sample - long currentTimeMillis = SystemClock.elapsedRealtime(); - if (mLastPushTimeMillisLocked - > currentTimeMillis - mConfig.getCachedMinTimeIntervalBetweenSamplesMillis()) { - int count = mSkippedSampleCountLocked.get(callType, /*valueOfKeyIfNotFound=*/ 0); - ++count; - mSkippedSampleCountLocked.put(callType, count); - return false; - } - - return true; - } - - /** - * Checks if the stats should be "sampled" - * - * @param samplingInterval sampling interval - * @return if the stats should be sampled - */ - private boolean shouldSample(int samplingInterval) { - if (samplingInterval <= 0) { - return false; - } - - return mRng.nextInt((int) samplingInterval) == 0; - } - - /** - * Finds the UID of the {@code packageName}. Returns {@link Process#INVALID_UID} if unable to - * find the UID. - */ - @GuardedBy("mLock") - private int getPackageUidAsUserLocked(@NonNull String packageName) { - Integer packageUid = mPackageUidCacheLocked.get(packageName); - if (packageUid == null) { - packageUid = PackageUtil.getPackageUid(mUserContext, packageName); - if (packageUid != Process.INVALID_UID) { - mPackageUidCacheLocked.put(packageName, packageUid); - } - } - return packageUid; - } - - /** Returns sampling ratio for stats type specified form {@link AppSearchConfig}. */ - private int getSamplingIntervalFromConfig(@CallStats.CallType int statsType) { - switch (statsType) { - case CallStats.CALL_TYPE_PUT_DOCUMENTS: - case CallStats.CALL_TYPE_GET_DOCUMENTS: - case CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_ID: - case CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH: - return mConfig.getCachedSamplingIntervalForBatchCallStats(); - case CallStats.CALL_TYPE_PUT_DOCUMENT: - return mConfig.getCachedSamplingIntervalForPutDocumentStats(); - case CallStats.CALL_TYPE_INITIALIZE: - return mConfig.getCachedSamplingIntervalForInitializeStats(); - case CallStats.CALL_TYPE_SEARCH: - return mConfig.getCachedSamplingIntervalForSearchStats(); - case CallStats.CALL_TYPE_GLOBAL_SEARCH: - return mConfig.getCachedSamplingIntervalForGlobalSearchStats(); - case CallStats.CALL_TYPE_OPTIMIZE: - return mConfig.getCachedSamplingIntervalForOptimizeStats(); - case CallStats.CALL_TYPE_UNKNOWN: - case CallStats.CALL_TYPE_SET_SCHEMA: - case CallStats.CALL_TYPE_GET_DOCUMENT: - case CallStats.CALL_TYPE_REMOVE_DOCUMENT_BY_ID: - case CallStats.CALL_TYPE_FLUSH: - case CallStats.CALL_TYPE_REMOVE_DOCUMENT_BY_SEARCH: - // TODO(b/173532925) Some of them above will have dedicated sampling ratio config - default: - return mConfig.getCachedSamplingIntervalDefault(); - } - } - - // - // Functions below are used for tests only - // - @VisibleForTesting - @GuardedBy("mLock") - void setLastPushTimeMillisLocked(long lastPushElapsedTimeMillis) { - mLastPushTimeMillisLocked = lastPushElapsedTimeMillis; - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/stats/StatsCollector.java b/apex/appsearch/service/java/com/android/server/appsearch/stats/StatsCollector.java deleted file mode 100644 index dd56739cde57d..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/stats/StatsCollector.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * 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.stats; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.UserIdInt; -import android.app.StatsManager; -import android.content.Context; -import android.os.UserHandle; -import android.util.Log; -import android.util.StatsEvent; - -import com.android.server.appsearch.AppSearchUserInstance; -import com.android.server.appsearch.AppSearchUserInstanceManager; - -import com.google.android.icing.proto.DocumentStorageInfoProto; -import com.google.android.icing.proto.IndexStorageInfoProto; -import com.google.android.icing.proto.SchemaStoreStorageInfoProto; -import com.google.android.icing.proto.StorageInfoProto; - -import java.util.List; -import java.util.Objects; -import java.util.concurrent.Executor; - -/** - * Implements statsd pullers for AppSearch. - * - *

    This class registers pullers to statsd, which will be called once a day to obtain AppSearch - * statistics that cannot be sent to statsd in real time by {@link PlatformLogger}. - * - * @hide - */ -public final class StatsCollector implements StatsManager.StatsPullAtomCallback { - private static final String TAG = "AppSearchStatsCollector"; - - private static volatile StatsCollector sStatsCollector; - private final StatsManager mStatsManager; - - /** - * Gets an instance of {@link StatsCollector} to be used. - * - *

    If no instance has been initialized yet, a new one will be created. Otherwise, the - * existing instance will be returned. - */ - @NonNull - public static StatsCollector getInstance(@NonNull Context context, - @NonNull Executor executor) { - Objects.requireNonNull(context); - Objects.requireNonNull(executor); - if (sStatsCollector == null) { - synchronized (StatsCollector.class) { - if (sStatsCollector == null) { - sStatsCollector = new StatsCollector(context, executor); - } - } - } - return sStatsCollector; - } - - private StatsCollector(@NonNull Context context, @NonNull Executor executor) { - mStatsManager = context.getSystemService(StatsManager.class); - if (mStatsManager != null) { - registerAtom(AppSearchStatsLog.APP_SEARCH_STORAGE_INFO, /*policy=*/ null, executor); - Log.d(TAG, "atoms registered"); - } else { - Log.e(TAG, "could not get StatsManager, atoms not registered"); - } - } - - /** - * {@inheritDoc} - * - * @return {@link StatsManager#PULL_SUCCESS} with list of atoms (potentially empty) if pull - * succeeded, {@link StatsManager#PULL_SKIP} if pull was too frequent or atom ID is - * unexpected. - */ - @Override - public int onPullAtom(int atomTag, @NonNull List data) { - Objects.requireNonNull(data); - switch (atomTag) { - case AppSearchStatsLog.APP_SEARCH_STORAGE_INFO: - return pullAppSearchStorageInfo(data); - default: - Log.e(TAG, "unexpected atom ID " + atomTag); - return StatsManager.PULL_SKIP; - } - } - - private static int pullAppSearchStorageInfo(@NonNull List data) { - AppSearchUserInstanceManager userInstanceManager = - AppSearchUserInstanceManager.getInstance(); - List userHandles = userInstanceManager.getAllUserHandles(); - for (int i = 0; i < userHandles.size(); i++) { - UserHandle userHandle = userHandles.get(i); - try { - AppSearchUserInstance userInstance = userInstanceManager.getUserInstance( - userHandle); - StorageInfoProto storageInfoProto = - userInstance.getAppSearchImpl().getRawStorageInfoProto(); - data.add(buildStatsEvent(userHandle.getIdentifier(), storageInfoProto)); - } catch (Throwable t) { - Log.e(TAG, - "Failed to pull the storage info for user " + userHandle.toString(), - t); - } - } - - // Skip the report if there is no data. - if (data.isEmpty()) { - return StatsManager.PULL_SKIP; - } - - return StatsManager.PULL_SUCCESS; - } - - /** - * Registers and configures the callback for the pulled atom. - * - * @param atomId The id of the atom - * @param policy Optional metadata specifying the timeout, cool down time etc. statsD would - * use default values if it is null - * @param executor The executor in which to run the callback - */ - private void registerAtom(int atomId, @Nullable StatsManager.PullAtomMetadata policy, - @NonNull Executor executor) { - mStatsManager.setPullAtomCallback(atomId, policy, executor, /*callback=*/this); - } - - private static StatsEvent buildStatsEvent(@UserIdInt int userId, - @NonNull StorageInfoProto storageInfoProto) { - return AppSearchStatsLog.buildStatsEvent( - AppSearchStatsLog.APP_SEARCH_STORAGE_INFO, - userId, - storageInfoProto.getTotalStorageSize(), - getDocumentStorageInfoBytes(storageInfoProto.getDocumentStorageInfo()), - getSchemaStoreStorageInfoBytes(storageInfoProto.getSchemaStoreStorageInfo()), - getIndexStorageInfoBytes(storageInfoProto.getIndexStorageInfo())); - } - - private static byte[] getDocumentStorageInfoBytes( - @NonNull DocumentStorageInfoProto proto) { - // Make sure we only log the fields defined in the atom in case new fields are added in - // IcingLib - DocumentStorageInfoProto.Builder builder = DocumentStorageInfoProto.newBuilder(); - builder.setNumAliveDocuments(proto.getNumAliveDocuments()) - .setNumDeletedDocuments(proto.getNumDeletedDocuments()) - .setNumExpiredDocuments(proto.getNumExpiredDocuments()) - .setDocumentStoreSize(proto.getDocumentStoreSize()) - .setDocumentLogSize(proto.getDocumentLogSize()) - .setKeyMapperSize(proto.getKeyMapperSize()) - .setDocumentIdMapperSize(proto.getDocumentIdMapperSize()) - .setScoreCacheSize(proto.getScoreCacheSize()) - .setFilterCacheSize(proto.getFilterCacheSize()) - .setCorpusMapperSize(proto.getCorpusMapperSize()) - .setCorpusScoreCacheSize(proto.getCorpusScoreCacheSize()) - .setNamespaceIdMapperSize(proto.getNamespaceIdMapperSize()) - .setNumNamespaces(proto.getNumNamespaces()); - return builder.build().toByteArray(); - } - - private static byte[] getSchemaStoreStorageInfoBytes( - @NonNull SchemaStoreStorageInfoProto proto) { - // Make sure we only log the fields defined in the atom in case new fields are added in - // IcingLib - SchemaStoreStorageInfoProto.Builder builder = SchemaStoreStorageInfoProto.newBuilder(); - builder.setSchemaStoreSize(proto.getSchemaStoreSize()) - .setNumSchemaTypes(proto.getNumSchemaTypes()) - .setNumTotalSections(proto.getNumTotalSections()) - .setNumSchemaTypesSectionsExhausted(proto.getNumSchemaTypesSectionsExhausted()); - return builder.build().toByteArray(); - } - - private static byte[] getIndexStorageInfoBytes( - @NonNull IndexStorageInfoProto proto) { - // Make sure we only log the fields defined in the atom in case new fields are added in - // IcingLib - IndexStorageInfoProto.Builder builder = IndexStorageInfoProto.newBuilder(); - builder.setIndexSize(proto.getIndexSize()) - .setLiteIndexLexiconSize(proto.getLiteIndexLexiconSize()) - .setLiteIndexHitBufferSize(proto.getLiteIndexHitBufferSize()) - .setMainIndexLexiconSize(proto.getMainIndexLexiconSize()) - .setMainIndexStorageSize(proto.getMainIndexStorageSize()) - .setMainIndexBlockSize(proto.getMainIndexBlockSize()) - .setNumBlocks(proto.getNumBlocks()) - .setMinFreeFraction(proto.getMinFreeFraction()); - return builder.build().toByteArray(); - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/util/PackageUtil.java b/apex/appsearch/service/java/com/android/server/appsearch/util/PackageUtil.java deleted file mode 100644 index 714ffb6c8b689..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/util/PackageUtil.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.util; - -import android.annotation.NonNull; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.Process; - -/** - * Utilities for interacting with {@link android.content.pm.PackageManager}, - * {@link android.os.UserHandle}, and other parts of dealing with apps and binder. - * - * @hide - */ -public class PackageUtil { - private PackageUtil() {} - - /** - * Finds the UID of the {@code packageName} in the given {@code context}. Returns - * {@link Process#INVALID_UID} if unable to find the UID. - */ - public static int getPackageUid(@NonNull Context context, @NonNull String packageName) { - try { - return context.getPackageManager().getPackageUid(packageName, /*flags=*/ 0); - } catch (PackageManager.NameNotFoundException e) { - return Process.INVALID_UID; - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotDisplayedBySystemMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotDisplayedBySystemMap.java deleted file mode 100644 index 95905af76123b..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotDisplayedBySystemMap.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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.visibilitystore; - -import android.annotation.NonNull; -import android.util.ArrayMap; - -import java.util.Map; -import java.util.Set; - -/** - * Stores information about what types are hidden from platform surfaces through the - * {@link android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} API. - * - * This object is not thread safe. - */ -class NotDisplayedBySystemMap { - /** - * Maps packages to databases to the set of prefixed schemas that are platform-hidden within - * that database. - */ - private final Map>> mMap = new ArrayMap<>(); - - /** - * Sets the prefixed schemas that are opted out of platform surfacing for the database. - * - *

    Any existing mappings for this prefix are overwritten. - */ - public void setNotDisplayedBySystem( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull Set prefixedSchemas) { - Map> databaseToSchemas = mMap.get(packageName); - if (databaseToSchemas == null) { - databaseToSchemas = new ArrayMap<>(); - mMap.put(packageName, databaseToSchemas); - } - databaseToSchemas.put(databaseName, prefixedSchemas); - } - - /** - * Returns whether the given prefixed schema is platform surfaceable (has not opted out) in the - * given database. - */ - public boolean isSchemaDisplayedBySystem( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String prefixedSchema) { - Map> databaseToSchemaType = mMap.get(packageName); - if (databaseToSchemaType == null) { - // No opt-outs for this package - return true; - } - Set schemaTypes = databaseToSchemaType.get(databaseName); - if (schemaTypes == null) { - // No opt-outs for this database - return true; - } - // Some schemas were opted out of being platform-surfaced. As long as this schema - // isn't one of those opt-outs, it's surfaceable. - return !schemaTypes.contains(prefixedSchema); - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java deleted file mode 100644 index b0108704be34a..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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.visibilitystore; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.AppSearchSchema; -import android.app.appsearch.GenericDocument; - -/** Holds the visibility settings that apply to a package's databases. */ -class VisibilityDocument extends GenericDocument { - /** Schema type for documents that hold AppSearch's metadata, e.g. visibility settings */ - public static final String SCHEMA_TYPE = "VisibilityType"; - - /** - * Property that holds the list of platform-hidden schemas, as part of the visibility settings. - */ - private static final String NOT_DISPLAYED_BY_SYSTEM_PROPERTY = "notPlatformSurfaceable"; - - /** Property that holds nested documents of package accessible schemas. */ - private static final String VISIBLE_TO_PACKAGES_PROPERTY = "packageAccessible"; - - /** - * Schema for the VisibilityStore's documents. - * - *

    NOTE: If you update this, also update - * {@link com.android.server.appsearch.external.localstorage.VisibilityStore#SCHEMA_VERSION} - */ - public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE) - .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( - NOT_DISPLAYED_BY_SYSTEM_PROPERTY) - .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) - .build()) - .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder( - VISIBLE_TO_PACKAGES_PROPERTY, VisibleToPackagesDocument.SCHEMA_TYPE) - .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) - .build()) - .build(); - - public VisibilityDocument(@NonNull GenericDocument genericDocument) { - super(genericDocument); - } - - @Nullable - public String[] getNotDisplayedBySystem() { - return getPropertyStringArray(NOT_DISPLAYED_BY_SYSTEM_PROPERTY); - } - - @Nullable - public GenericDocument[] getVisibleToPackages() { - return getPropertyDocumentArray(VISIBLE_TO_PACKAGES_PROPERTY); - } - - /** Builder for {@link VisibilityDocument}. */ - public static class Builder extends GenericDocument.Builder { - public Builder(@NonNull String namespace, @NonNull String id) { - super(namespace, id, SCHEMA_TYPE); - } - - /** Sets which prefixed schemas have opted out of platform surfacing. */ - @NonNull - public Builder setNotDisplayedBySystem(@NonNull String[] notDisplayedBySystemSchemas) { - return setPropertyString(NOT_DISPLAYED_BY_SYSTEM_PROPERTY, notDisplayedBySystemSchemas); - } - - /** Sets which prefixed schemas have configured package access. */ - @NonNull - public Builder setVisibleToPackages( - @NonNull VisibleToPackagesDocument[] visibleToPackagesDocuments) { - return setPropertyDocument(VISIBLE_TO_PACKAGES_PROPERTY, visibleToPackagesDocuments); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java deleted file mode 100644 index ce142d646d1c9..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * 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.visibilitystore; - -import static android.Manifest.permission.READ_GLOBAL_APP_SEARCH_DATA; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchResult; -import android.app.appsearch.AppSearchSchema; -import android.app.appsearch.GenericDocument; -import android.app.appsearch.GetSchemaResponse; -import android.app.appsearch.PackageIdentifier; -import android.app.appsearch.exceptions.AppSearchException; -import android.content.Context; -import android.content.pm.PackageManager; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.ArraySet; - -import com.android.server.appsearch.external.localstorage.AppSearchImpl; -import com.android.server.appsearch.external.localstorage.util.PrefixUtil; -import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore; -import com.android.server.appsearch.util.PackageUtil; - -import com.google.android.icing.proto.PersistType; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * Manages any visibility settings for all the package's databases that AppSearchImpl knows about. - * Persists the visibility settings and reloads them on initialization. - * - *

    The VisibilityStore creates a document for each package's databases. This document holds the - * visibility settings that apply to that package's database. The VisibilityStore also creates a - * schema for these documents and has its own package and database so that its data doesn't - * interfere with any clients' data. It persists the document and schema through AppSearchImpl. - * - *

    These visibility settings are used to ensure AppSearch queries respect the clients' settings - * on who their data is visible to. - * - *

    This class doesn't handle any locking itself. Its callers should handle the locking at a - * higher level. - * - * @hide - */ -public class VisibilityStoreImpl implements VisibilityStore { - /** Version for the visibility schema */ - private static final int SCHEMA_VERSION = 0; - - /** Namespace of documents that contain visibility settings */ - private static final String NAMESPACE = ""; - - /** Prefix to add to all visibility document ids. IcingSearchEngine doesn't allow empty ids. */ - private static final String ID_PREFIX = "uri:"; - - private final AppSearchImpl mAppSearchImpl; - - // Context of the user that the call is being made as. - private final Context mUserContext; - - /** Stores the schemas that are platform-hidden. All values are prefixed. */ - private final NotDisplayedBySystemMap mNotDisplayedBySystemMap = new NotDisplayedBySystemMap(); - - /** Stores the schemas that are visible to 3p packages. All values are prefixed. */ - private final VisibleToPackagesMap mVisibleToPackagesMap = new VisibleToPackagesMap(); - - /** - * Creates and initializes VisibilityStore. - * - * @param appSearchImpl AppSearchImpl instance - * @param userContext Context of the user that the call is being made as - */ - @NonNull - public static VisibilityStoreImpl create( - @NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext) - throws AppSearchException { - return new VisibilityStoreImpl(appSearchImpl, userContext); - } - - private VisibilityStoreImpl(@NonNull AppSearchImpl appSearchImpl, @NonNull Context userContext) - throws AppSearchException { - mAppSearchImpl = Objects.requireNonNull(appSearchImpl); - mUserContext = Objects.requireNonNull(userContext); - - GetSchemaResponse getSchemaResponse = mAppSearchImpl.getSchema(PACKAGE_NAME, DATABASE_NAME); - boolean hasVisibilityType = false; - boolean hasVisibleToPackagesType = false; - for (AppSearchSchema schema : getSchemaResponse.getSchemas()) { - if (schema.getSchemaType().equals(VisibilityDocument.SCHEMA_TYPE)) { - hasVisibilityType = true; - } else if (schema.getSchemaType().equals(VisibleToPackagesDocument.SCHEMA_TYPE)) { - hasVisibleToPackagesType = true; - } - - if (hasVisibilityType && hasVisibleToPackagesType) { - // Found both our types, can exit early. - break; - } - } - if (!hasVisibilityType || !hasVisibleToPackagesType) { - // Schema type doesn't exist yet. Add it. - mAppSearchImpl.setSchema( - PACKAGE_NAME, - DATABASE_NAME, - Arrays.asList(VisibilityDocument.SCHEMA, VisibleToPackagesDocument.SCHEMA), - /*visibilityStore=*/ null, // Avoid recursive calls - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ SCHEMA_VERSION); - } - - // Populate visibility settings set - for (Map.Entry> entry : - mAppSearchImpl.getPackageToDatabases().entrySet()) { - String packageName = entry.getKey(); - if (packageName.equals(PACKAGE_NAME)) { - continue; // Our own package. Skip. - } - - for (String databaseName : entry.getValue()) { - VisibilityDocument visibilityDocument; - try { - // Note: We use the other clients' prefixed names as ids - visibilityDocument = - new VisibilityDocument( - mAppSearchImpl.getDocument( - PACKAGE_NAME, - DATABASE_NAME, - NAMESPACE, - /*id=*/ getVisibilityDocumentId( - packageName, databaseName), - /*typePropertyPaths=*/ Collections.emptyMap())); - } catch (AppSearchException e) { - if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) { - // TODO(b/172068212): This indicates some desync error. We were expecting a - // document, but didn't find one. Should probably reset AppSearch instead - // of ignoring it. - continue; - } - // Otherwise, this is some other error we should pass up. - throw e; - } - - // Update platform visibility settings - String[] notDisplayedBySystemSchemas = visibilityDocument.getNotDisplayedBySystem(); - if (notDisplayedBySystemSchemas != null) { - mNotDisplayedBySystemMap.setNotDisplayedBySystem( - packageName, - databaseName, - new ArraySet<>(notDisplayedBySystemSchemas)); - } - - // Update 3p package visibility settings - Map> schemaToPackageIdentifierMap = new ArrayMap<>(); - GenericDocument[] visibleToPackagesDocuments = - visibilityDocument.getVisibleToPackages(); - if (visibleToPackagesDocuments != null) { - for (int i = 0; i < visibleToPackagesDocuments.length; i++) { - VisibleToPackagesDocument visibleToPackagesDocument = - new VisibleToPackagesDocument(visibleToPackagesDocuments[i]); - PackageIdentifier packageIdentifier = - visibleToPackagesDocument.getPackageIdentifier(); - String prefixedSchema = visibleToPackagesDocument.getAccessibleSchemaType(); - Set packageIdentifiers = - schemaToPackageIdentifierMap.get(prefixedSchema); - if (packageIdentifiers == null) { - packageIdentifiers = new ArraySet<>(); - } - packageIdentifiers.add(packageIdentifier); - schemaToPackageIdentifierMap.put(prefixedSchema, packageIdentifiers); - } - } - mVisibleToPackagesMap.setVisibleToPackages( - packageName, databaseName, schemaToPackageIdentifierMap); - } - } - } - - @Override - public void setVisibility( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull Set schemasNotDisplayedBySystem, - @NonNull Map> schemasVisibleToPackages) - throws AppSearchException { - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(schemasNotDisplayedBySystem); - Objects.requireNonNull(schemasVisibleToPackages); - - // Persist the document - VisibilityDocument.Builder visibilityDocument = - new VisibilityDocument.Builder( - NAMESPACE, /*id=*/ getVisibilityDocumentId(packageName, databaseName)); - if (!schemasNotDisplayedBySystem.isEmpty()) { - visibilityDocument.setNotDisplayedBySystem( - schemasNotDisplayedBySystem.toArray(new String[0])); - } - - Map> schemaToPackageIdentifierMap = new ArrayMap<>(); - List visibleToPackagesDocuments = new ArrayList<>(); - for (Map.Entry> entry : - schemasVisibleToPackages.entrySet()) { - for (int i = 0; i < entry.getValue().size(); i++) { - VisibleToPackagesDocument visibleToPackagesDocument = - new VisibleToPackagesDocument.Builder(NAMESPACE, /*id=*/ "") - .setAccessibleSchemaType(entry.getKey()) - .setPackageIdentifier(entry.getValue().get(i)) - .build(); - visibleToPackagesDocuments.add(visibleToPackagesDocument); - } - schemaToPackageIdentifierMap.put(entry.getKey(), new ArraySet<>(entry.getValue())); - } - if (!visibleToPackagesDocuments.isEmpty()) { - visibilityDocument.setVisibleToPackages( - visibleToPackagesDocuments.toArray(new VisibleToPackagesDocument[0])); - } - - mAppSearchImpl.putDocument( - PACKAGE_NAME, DATABASE_NAME, visibilityDocument.build(), /*logger=*/ null); - // Now that the visibility document has been written. Persist the newly written data. - mAppSearchImpl.persistToDisk(PersistType.Code.LITE); - - // Update derived data structures. - mNotDisplayedBySystemMap.setNotDisplayedBySystem( - packageName, databaseName, schemasNotDisplayedBySystem); - mVisibleToPackagesMap.setVisibleToPackages( - packageName, databaseName, schemaToPackageIdentifierMap); - } - - /** - * Checks whether the given package has access to system-surfaceable schemas. - * - * @param callerPackageName Package name of the caller. - */ - public boolean doesCallerHaveSystemAccess(@NonNull String callerPackageName) { - Objects.requireNonNull(callerPackageName); - return mUserContext.getPackageManager() - .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, callerPackageName) - == PackageManager.PERMISSION_GRANTED; - } - - @Override - public boolean isSchemaSearchableByCaller( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String prefixedSchema, - int callerUid, - boolean callerHasSystemAccess) { - Objects.requireNonNull(packageName); - Objects.requireNonNull(databaseName); - Objects.requireNonNull(prefixedSchema); - - if (packageName.equals(PACKAGE_NAME)) { - return false; // VisibilityStore schemas are for internal bookkeeping. - } - - if (callerHasSystemAccess - && mNotDisplayedBySystemMap.isSchemaDisplayedBySystem( - packageName, databaseName, prefixedSchema)) { - return true; - } - - // May not be platform surfaceable, but might still be accessible through 3p access. - return isSchemaVisibleToPackages(packageName, databaseName, prefixedSchema, callerUid); - } - - /** - * Returns whether the schema is accessible by the {@code callerUid}. Checks that the callerUid - * has one of the allowed PackageIdentifier's package. And if so, that the package also has the - * matching certificate. - * - *

    This supports packages that have certificate rotation. As long as the specified - * certificate was once used to sign the package, the package will still be granted access. This - * does not handle packages that have been signed by multiple certificates. - */ - private boolean isSchemaVisibleToPackages( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String prefixedSchema, - int callerUid) { - Set packageIdentifiers = - mVisibleToPackagesMap.getAccessiblePackages( - packageName, databaseName, prefixedSchema); - if (packageIdentifiers.isEmpty()) { - return false; - } - for (PackageIdentifier packageIdentifier : packageIdentifiers) { - // TODO(b/169883602): Consider caching the UIDs of packages. Looking this up in the - // package manager could be costly. We would also need to update the cache on - // package-removals. - - // 'callerUid' is the uid of the caller. The 'user' doesn't have to be the same one as - // the callerUid since clients can createContextAsUser with some other user, and then - // make calls to us. So just check if the appId portion of the uid is the same. This is - // essentially UserHandle.isSameApp, but that's not a system API for us to use. - int callerAppId = UserHandle.getAppId(callerUid); - int packageUid = - PackageUtil.getPackageUid(mUserContext, packageIdentifier.getPackageName()); - int userAppId = UserHandle.getAppId(packageUid); - if (callerAppId != userAppId) { - continue; - } - - // Check that the package also has the matching certificate - if (mUserContext - .getPackageManager() - .hasSigningCertificate( - packageIdentifier.getPackageName(), - packageIdentifier.getSha256Certificate(), - PackageManager.CERT_INPUT_SHA256)) { - // The caller has the right package name and right certificate! - return true; - } - } - // If we can't verify the schema is package accessible, default to no access. - return false; - } - - /** - * Adds a prefix to create a visibility store document's id. - * - * @param packageName Package to which the visibility doc refers - * @param databaseName Database to which the visibility doc refers - * @return Prefixed id - */ - @NonNull - private static String getVisibilityDocumentId( - @NonNull String packageName, @NonNull String databaseName) { - return ID_PREFIX + PrefixUtil.createPrefix(packageName, databaseName); - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesDocument.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesDocument.java deleted file mode 100644 index 8d503390eaaac..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesDocument.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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.visibilitystore; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.AppSearchSchema; -import android.app.appsearch.GenericDocument; -import android.app.appsearch.PackageIdentifier; - -/** - * Holds configuration about a package+cert that can access a schema. - * - * @see android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage - */ -class VisibleToPackagesDocument extends GenericDocument { - /** Schema type for nested documents that hold package accessible information. */ - public static final String SCHEMA_TYPE = "PackageAccessibleType"; - - /** Property that holds the package name that can access a schema. */ - private static final String PACKAGE_NAME_PROPERTY = "packageName"; - - /** Property that holds the SHA 256 certificate of the app that can access a schema. */ - private static final String SHA_256_CERT_PROPERTY = "sha256Cert"; - - /** Property that holds the prefixed schema type that is accessible by some package. */ - private static final String ACCESSIBLE_SCHEMA_PROPERTY = "accessibleSchema"; - - /** - * Schema for package accessible documents, these will be nested in a top-level - * {@link VisibilityDocument}. - * - *

    NOTE: If you update this, also update - * {@link com.android.server.appsearch.external.localstorage.VisibilityStore#SCHEMA_VERSION} - */ - public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE) - .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(PACKAGE_NAME_PROPERTY) - .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .build()) - .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(SHA_256_CERT_PROPERTY) - .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .build()) - .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( - ACCESSIBLE_SCHEMA_PROPERTY) - .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .build()) - .build(); - - VisibleToPackagesDocument(@NonNull GenericDocument genericDocument) { - super(genericDocument); - } - - @Nullable - public String getAccessibleSchemaType() { - return getPropertyString(ACCESSIBLE_SCHEMA_PROPERTY); - } - - /** Gets which package is able to access {@link #getAccessibleSchemaType} */ - @NonNull - public PackageIdentifier getPackageIdentifier() { - String packageName = getPropertyString(PACKAGE_NAME_PROPERTY); - byte[] sha256Cert = getPropertyBytes(SHA_256_CERT_PROPERTY); - return new PackageIdentifier(packageName, sha256Cert); - } - - /** Builder for {@link VisibleToPackagesDocument} instances. */ - public static class Builder extends GenericDocument.Builder { - Builder(@NonNull String namespace, @NonNull String id) { - super(namespace, id, SCHEMA_TYPE); - } - - /** Sets which prefixed schema type is accessible by the package */ - @NonNull - public Builder setAccessibleSchemaType(@NonNull String schemaType) { - return setPropertyString(ACCESSIBLE_SCHEMA_PROPERTY, schemaType); - } - - /** Sets which package is able to access the {@link #setAccessibleSchemaType}. */ - @NonNull - public Builder setPackageIdentifier(@NonNull PackageIdentifier packageIdentifier) { - return setPropertyString(PACKAGE_NAME_PROPERTY, packageIdentifier.getPackageName()) - .setPropertyBytes(SHA_256_CERT_PROPERTY, - packageIdentifier.getSha256Certificate()); - } - - @Override - @NonNull - public VisibleToPackagesDocument build() { - return new VisibleToPackagesDocument(super.build()); - } - } -} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesMap.java deleted file mode 100644 index e2b51a75025c8..0000000000000 --- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibleToPackagesMap.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.visibilitystore; - -import android.annotation.NonNull; -import android.app.appsearch.PackageIdentifier; -import android.util.ArrayMap; - -import java.util.Collections; -import java.util.Map; -import java.util.Set; - -/** - * Stores information about what types are accessible to which packages through the - * {@link android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} API. - * - * This object is not thread safe. - */ -class VisibleToPackagesMap { - /** - * Maps packages to databases to prefixed schemas to PackageIdentifiers that have access to that - * schema. - */ - private final Map>>> mMap = - new ArrayMap<>(); - - /** - * Sets the prefixed schemas that have package visibility in the given database. - * - *

    Any existing mappings for this prefix are overwritten. - */ - public void setVisibleToPackages( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull Map> schemaToPackageIdentifier) { - Map>> databaseToSchemaTypeToVisibility = - mMap.get(packageName); - if (databaseToSchemaTypeToVisibility == null) { - databaseToSchemaTypeToVisibility = new ArrayMap<>(); - mMap.put(packageName, databaseToSchemaTypeToVisibility); - } - databaseToSchemaTypeToVisibility.put(databaseName, schemaToPackageIdentifier); - } - - /** - * Returns the set of all {@link android.app.appsearch.PackageIdentifier}s which can access the - * given schema type. - * - *

    If no such settings exist, returns the empty set. - */ - @NonNull - public Set getAccessiblePackages( - @NonNull String packageName, - @NonNull String databaseName, - @NonNull String prefixedSchema) { - Map>> databaseToSchemaTypeToVisibility = - mMap.get(packageName); - if (databaseToSchemaTypeToVisibility == null) { - return Collections.emptySet(); - } - Map> schemaTypeToVisibility = - databaseToSchemaTypeToVisibility.get(databaseName); - if (schemaTypeToVisibility == null) { - return Collections.emptySet(); - } - Set accessiblePackages = schemaTypeToVisibility.get(prefixedSchema); - if (accessiblePackages == null) { - return Collections.emptySet(); - } - return accessiblePackages; - } -} diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt deleted file mode 100644 index a81d7d8022b23..0000000000000 --- a/apex/appsearch/synced_jetpack_changeid.txt +++ /dev/null @@ -1 +0,0 @@ -Ie04f1ecc033faae8085afcb51eb9e40a298998d5 diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp deleted file mode 100644 index 5407cb4ccec78..0000000000000 --- a/apex/appsearch/testing/Android.bp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (C) 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_license"], -} - -java_library { - name: "AppSearchTestUtils", - srcs: ["java/**/*.java"], - libs: [ - "androidx.test.ext.junit", - "framework", - "framework-appsearch", - "guava", - "truth-prebuilt", - ], - visibility: [ - "//frameworks/base/core/tests/coretests", - "//cts/hostsidetests/appsearch", - "//cts/tests:__subpackages__", - "//vendor:__subpackages__", - ], -} diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java deleted file mode 100644 index 71b4f36a71279..0000000000000 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java +++ /dev/null @@ -1,229 +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 com.android.server.appsearch.testing; - -import android.annotation.NonNull; -import android.annotation.UserIdInt; -import android.app.appsearch.AppSearchBatchResult; -import android.app.appsearch.AppSearchManager; -import android.app.appsearch.AppSearchResult; -import android.app.appsearch.AppSearchSession; -import android.app.appsearch.AppSearchSessionShim; -import android.app.appsearch.BatchResultCallback; -import android.app.appsearch.GenericDocument; -import android.app.appsearch.GetByDocumentIdRequest; -import android.app.appsearch.GetSchemaResponse; -import android.app.appsearch.PutDocumentsRequest; -import android.app.appsearch.RemoveByDocumentIdRequest; -import android.app.appsearch.ReportUsageRequest; -import android.app.appsearch.SearchResults; -import android.app.appsearch.SearchResultsShim; -import android.app.appsearch.SearchSpec; -import android.app.appsearch.SetSchemaRequest; -import android.app.appsearch.SetSchemaResponse; -import android.app.appsearch.StorageInfo; -import android.app.appsearch.exceptions.AppSearchException; -import android.content.Context; -import android.os.UserHandle; - -import androidx.test.core.app.ApplicationProvider; - -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; - -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * This test class adapts the AppSearch Framework API to ListenableFuture, so it can be tested via - * a consistent interface. - * @hide - */ -public class AppSearchSessionShimImpl implements AppSearchSessionShim { - private final AppSearchSession mAppSearchSession; - private final ExecutorService mExecutor; - - /** Creates the SearchSessionShim with given SearchContext. */ - @NonNull - public static ListenableFuture createSearchSession( - @NonNull AppSearchManager.SearchContext searchContext) { - Context context = ApplicationProvider.getApplicationContext(); - return createSearchSession(context, searchContext, Executors.newCachedThreadPool()); - } - - /** Creates the SearchSessionShim with given SearchContext for the given user. */ - @NonNull - public static ListenableFuture createSearchSession( - @NonNull AppSearchManager.SearchContext searchContext, @UserIdInt int userId) { - Context context = ApplicationProvider.getApplicationContext() - .createContextAsUser(new UserHandle(userId), /*flags=*/ 0); - return createSearchSession(context, searchContext, Executors.newCachedThreadPool()); - } - - /** Creates the SearchSession with given Context and ExecutorService. */ - @NonNull - public static ListenableFuture createSearchSession( - @NonNull Context context, - @NonNull AppSearchManager.SearchContext searchContext, - @NonNull ExecutorService executor) { - AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class); - SettableFuture> future = SettableFuture.create(); - appSearchManager.createSearchSession(searchContext, executor, future::set); - return Futures.transform( - future, - instance -> new AppSearchSessionShimImpl(instance.getResultValue(), executor), - executor); - } - - private AppSearchSessionShimImpl( - @NonNull AppSearchSession session, @NonNull ExecutorService executor) { - mAppSearchSession = Objects.requireNonNull(session); - mExecutor = Objects.requireNonNull(executor); - } - - @Override - @NonNull - public ListenableFuture setSchema(@NonNull SetSchemaRequest request) { - SettableFuture> future = SettableFuture.create(); - mAppSearchSession.setSchema(request, mExecutor, mExecutor, future::set); - return Futures.transformAsync(future, this::transformResult, mExecutor); - } - - @Override - @NonNull - public ListenableFuture getSchema() { - SettableFuture> future = SettableFuture.create(); - mAppSearchSession.getSchema(mExecutor, future::set); - return Futures.transformAsync(future, this::transformResult, mExecutor); - } - - @NonNull - @Override - public ListenableFuture> getNamespaces() { - SettableFuture>> future = SettableFuture.create(); - mAppSearchSession.getNamespaces(mExecutor, future::set); - return Futures.transformAsync(future, this::transformResult, mExecutor); - } - - @Override - @NonNull - public ListenableFuture> put( - @NonNull PutDocumentsRequest request) { - SettableFuture> future = SettableFuture.create(); - mAppSearchSession.put( - request, mExecutor, new BatchResultCallbackAdapter<>(future)); - return future; - } - - @Override - @NonNull - public ListenableFuture> getByDocumentId( - @NonNull GetByDocumentIdRequest request) { - SettableFuture> future = - SettableFuture.create(); - mAppSearchSession.getByDocumentId( - request, mExecutor, new BatchResultCallbackAdapter<>(future)); - return future; - } - - @Override - @NonNull - public SearchResultsShim search( - @NonNull String queryExpression, @NonNull SearchSpec searchSpec) { - SearchResults searchResults = mAppSearchSession.search(queryExpression, searchSpec); - return new SearchResultsShimImpl(searchResults, mExecutor); - } - - @Override - @NonNull - public ListenableFuture reportUsage(@NonNull ReportUsageRequest request) { - SettableFuture> future = SettableFuture.create(); - mAppSearchSession.reportUsage(request, mExecutor, future::set); - return Futures.transformAsync(future, this::transformResult, mExecutor); - } - - @Override - @NonNull - public ListenableFuture> remove( - @NonNull RemoveByDocumentIdRequest request) { - SettableFuture> future = SettableFuture.create(); - mAppSearchSession.remove(request, mExecutor, new BatchResultCallbackAdapter<>(future)); - return future; - } - - @Override - @NonNull - public ListenableFuture remove( - @NonNull String queryExpression, @NonNull SearchSpec searchSpec) { - SettableFuture> future = SettableFuture.create(); - mAppSearchSession.remove(queryExpression, searchSpec, mExecutor, future::set); - return Futures.transformAsync(future, this::transformResult, mExecutor); - } - - @NonNull - @Override - public ListenableFuture getStorageInfo() { - SettableFuture> future = SettableFuture.create(); - mAppSearchSession.getStorageInfo(mExecutor, future::set); - return Futures.transformAsync(future, this::transformResult, mExecutor); - } - - @Override - public void close() { - mAppSearchSession.close(); - } - - @Override - @NonNull - public ListenableFuture requestFlush() { - SettableFuture> future = SettableFuture.create(); - // The data in platform will be flushed by scheduled task. AppSearchSession won't do - // anything extra flush. - future.set(AppSearchResult.newSuccessfulResult(null)); - return Futures.transformAsync(future, this::transformResult, mExecutor); - } - - private ListenableFuture transformResult( - @NonNull AppSearchResult result) throws AppSearchException { - if (!result.isSuccess()) { - throw new AppSearchException(result.getResultCode(), result.getErrorMessage()); - } - return Futures.immediateFuture(result.getResultValue()); - } - - private static final class BatchResultCallbackAdapter - implements BatchResultCallback { - private final SettableFuture> mFuture; - - BatchResultCallbackAdapter(SettableFuture> future) { - mFuture = future; - } - - @Override - public void onResult(AppSearchBatchResult result) { - mFuture.set(result); - } - - @Override - public void onSystemError(Throwable t) { - mFuture.setException(t); - } - } -} diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java deleted file mode 100644 index c35849d23c26b..0000000000000 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java +++ /dev/null @@ -1,104 +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 com.android.server.appsearch.testing; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchManager; -import android.app.appsearch.AppSearchResult; -import android.app.appsearch.GlobalSearchSession; -import android.app.appsearch.GlobalSearchSessionShim; -import android.app.appsearch.ReportSystemUsageRequest; -import android.app.appsearch.SearchResults; -import android.app.appsearch.SearchResultsShim; -import android.app.appsearch.SearchSpec; -import android.app.appsearch.exceptions.AppSearchException; -import android.content.Context; - -import androidx.test.core.app.ApplicationProvider; - -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; - -import java.util.Objects; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * This test class adapts the AppSearch Framework API to ListenableFuture, so it can be tested via a - * consistent interface. - * - * @hide - */ -public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim { - private final GlobalSearchSession mGlobalSearchSession; - private final ExecutorService mExecutor; - - @NonNull - public static ListenableFuture createGlobalSearchSession() { - return createGlobalSearchSession(ApplicationProvider.getApplicationContext()); - } - - /** Only for use when called from a non-instrumented context. */ - @NonNull - public static ListenableFuture createGlobalSearchSession( - @NonNull Context context) { - AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class); - SettableFuture> future = SettableFuture.create(); - ExecutorService executor = Executors.newCachedThreadPool(); - appSearchManager.createGlobalSearchSession(executor, future::set); - return Futures.transform( - future, - instance -> new GlobalSearchSessionShimImpl(instance.getResultValue(), executor), - executor); - } - - private GlobalSearchSessionShimImpl( - @NonNull GlobalSearchSession session, @NonNull ExecutorService executor) { - mGlobalSearchSession = Objects.requireNonNull(session); - mExecutor = Objects.requireNonNull(executor); - } - - @NonNull - @Override - public SearchResultsShim search( - @NonNull String queryExpression, @NonNull SearchSpec searchSpec) { - SearchResults searchResults = mGlobalSearchSession.search(queryExpression, searchSpec); - return new SearchResultsShimImpl(searchResults, mExecutor); - } - - @NonNull - @Override - public ListenableFuture reportSystemUsage(@NonNull ReportSystemUsageRequest request) { - SettableFuture> future = SettableFuture.create(); - mGlobalSearchSession.reportSystemUsage(request, mExecutor, future::set); - return Futures.transformAsync(future, this::transformResult, mExecutor); - } - - @Override - public void close() { - mGlobalSearchSession.close(); - } - - private ListenableFuture transformResult( - @NonNull AppSearchResult result) throws AppSearchException { - if (!result.isSuccess()) { - throw new AppSearchException(result.getResultCode(), result.getErrorMessage()); - } - return Futures.immediateFuture(result.getResultValue()); - } -} diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java deleted file mode 100644 index 72078f855d706..0000000000000 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/SearchResultsShimImpl.java +++ /dev/null @@ -1,58 +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 com.android.server.appsearch.testing; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchResult; -import android.app.appsearch.SearchResult; -import android.app.appsearch.SearchResults; -import android.app.appsearch.SearchResultsShim; - -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; - -import java.util.List; -import java.util.Objects; -import java.util.concurrent.Executor; - -/** - * This test class adapts the AppSearch Framework API to ListenableFuture, so it can be tested via - * a consistent interface. - * @hide - */ -public class SearchResultsShimImpl implements SearchResultsShim { - private final Executor mExecutor; - private final SearchResults mSearchResults; - - SearchResultsShimImpl(@NonNull SearchResults searchResults, @NonNull Executor executor) { - mExecutor = Objects.requireNonNull(executor); - mSearchResults = Objects.requireNonNull(searchResults); - } - - @NonNull - public ListenableFuture> getNextPage() { - SettableFuture>> future = SettableFuture.create(); - mSearchResults.getNextPage(mExecutor, future::set); - return Futures.transform(future, AppSearchResult::getResultValue, mExecutor); - } - - @Override - public void close() { - mSearchResults.close(); - } -} diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchEmail.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchEmail.java deleted file mode 100644 index d28d4ac269d43..0000000000000 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchEmail.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.testing; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.AppSearchSchema; -import android.app.appsearch.AppSearchSchema.PropertyConfig; -import android.app.appsearch.AppSearchSchema.StringPropertyConfig; -import android.app.appsearch.GenericDocument; - -/** - * Encapsulates a {@link GenericDocument} that represent an email. - * - *

    This class is a higher level implement of {@link GenericDocument}. - */ -public class AppSearchEmail extends GenericDocument { - /** The name of the schema type for {@link AppSearchEmail} documents. */ - public static final String SCHEMA_TYPE = "builtin:Email"; - - private static final String KEY_FROM = "from"; - private static final String KEY_TO = "to"; - private static final String KEY_CC = "cc"; - private static final String KEY_BCC = "bcc"; - private static final String KEY_SUBJECT = "subject"; - private static final String KEY_BODY = "body"; - - public static final AppSearchSchema SCHEMA = - new AppSearchSchema.Builder(SCHEMA_TYPE) - .addProperty( - new StringPropertyConfig.Builder(KEY_FROM) - .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) - .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES) - .build()) - .addProperty( - new StringPropertyConfig.Builder(KEY_TO) - .setCardinality(PropertyConfig.CARDINALITY_REPEATED) - .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES) - .build()) - .addProperty( - new StringPropertyConfig.Builder(KEY_CC) - .setCardinality(PropertyConfig.CARDINALITY_REPEATED) - .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES) - .build()) - .addProperty( - new StringPropertyConfig.Builder(KEY_BCC) - .setCardinality(PropertyConfig.CARDINALITY_REPEATED) - .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES) - .build()) - .addProperty( - new StringPropertyConfig.Builder(KEY_SUBJECT) - .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) - .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES) - .build()) - .addProperty( - new StringPropertyConfig.Builder(KEY_BODY) - .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) - .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) - .setIndexingType(StringPropertyConfig.INDEXING_TYPE_PREFIXES) - .build()) - .build(); - - /** - * Creates a new {@link AppSearchEmail} from the contents of an existing {@link - * GenericDocument}. - * - * @param document The {@link GenericDocument} containing the email content. - */ - public AppSearchEmail(@NonNull GenericDocument document) { - super(document); - } - - /** - * Gets the from address of {@link AppSearchEmail}. - * - * @return The subject of {@link AppSearchEmail} or {@code null} if it's not been set yet. - */ - @Nullable - public String getFrom() { - return getPropertyString(KEY_FROM); - } - - /** - * Gets the destination addresses of {@link AppSearchEmail}. - * - * @return The destination addresses of {@link AppSearchEmail} or {@code null} if it's not been - * set yet. - */ - @Nullable - public String[] getTo() { - return getPropertyStringArray(KEY_TO); - } - - /** - * Gets the CC list of {@link AppSearchEmail}. - * - * @return The CC list of {@link AppSearchEmail} or {@code null} if it's not been set yet. - */ - @Nullable - public String[] getCc() { - return getPropertyStringArray(KEY_CC); - } - - /** - * Gets the BCC list of {@link AppSearchEmail}. - * - * @return The BCC list of {@link AppSearchEmail} or {@code null} if it's not been set yet. - */ - @Nullable - public String[] getBcc() { - return getPropertyStringArray(KEY_BCC); - } - - /** - * Gets the subject of {@link AppSearchEmail}. - * - * @return The value subject of {@link AppSearchEmail} or {@code null} if it's not been set yet. - */ - @Nullable - public String getSubject() { - return getPropertyString(KEY_SUBJECT); - } - - /** - * Gets the body of {@link AppSearchEmail}. - * - * @return The body of {@link AppSearchEmail} or {@code null} if it's not been set yet. - */ - @Nullable - public String getBody() { - return getPropertyString(KEY_BODY); - } - - /** The builder class for {@link AppSearchEmail}. */ - public static class Builder extends GenericDocument.Builder { - /** - * Creates a new {@link Builder} - * - * @param namespace The namespace of the Email. - * @param id The ID of the Email. - */ - public Builder(@NonNull String namespace, @NonNull String id) { - super(namespace, id, SCHEMA_TYPE); - } - - /** Sets the from address of {@link AppSearchEmail} */ - @NonNull - public Builder setFrom(@NonNull String from) { - return setPropertyString(KEY_FROM, from); - } - - /** Sets the destination address of {@link AppSearchEmail} */ - @NonNull - public Builder setTo(@NonNull String... to) { - return setPropertyString(KEY_TO, to); - } - - /** Sets the CC list of {@link AppSearchEmail} */ - @NonNull - public Builder setCc(@NonNull String... cc) { - return setPropertyString(KEY_CC, cc); - } - - /** Sets the BCC list of {@link AppSearchEmail} */ - @NonNull - public Builder setBcc(@NonNull String... bcc) { - return setPropertyString(KEY_BCC, bcc); - } - - /** Sets the subject of {@link AppSearchEmail} */ - @NonNull - public Builder setSubject(@NonNull String subject) { - return setPropertyString(KEY_SUBJECT, subject); - } - - /** Sets the body of {@link AppSearchEmail} */ - @NonNull - public Builder setBody(@NonNull String body) { - return setPropertyString(KEY_BODY, body); - } - - /** Builds the {@link AppSearchEmail} object. */ - @NonNull - @Override - public AppSearchEmail build() { - return new AppSearchEmail(super.build()); - } - } -} diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java deleted file mode 100644 index 7b74578adfc79..0000000000000 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java +++ /dev/null @@ -1,261 +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 android.annotation.NonNull; -import android.annotation.SuppressLint; - -import com.google.common.util.concurrent.ListenableFuture; - -import java.io.Closeable; -import java.util.Set; - -/** - * Provides a connection to a single AppSearch database. - * - *

    An {@link AppSearchSessionShim} instance provides access to database operations such as - * setting a schema, adding documents, and searching. - * - *

    Instances of this interface are usually obtained from a storage implementation, e.g. {@code - * AppSearchManager.createSearchSession()} or {@code PlatformStorage.createSearchSession()}. - * - *

    All implementations of this interface must be thread safe. - * - * @see GlobalSearchSessionShim - */ -public interface AppSearchSessionShim extends Closeable { - - /** - * Sets the schema that represents the organizational structure of data within the AppSearch - * database. - * - *

    Upon creating an {@link AppSearchSessionShim}, {@link #setSchema} should be called. If the - * schema needs to be updated, or it has not been previously set, then the provided schema will - * be saved and persisted to disk. Otherwise, {@link #setSchema} is handled efficiently as a - * no-op call. - * - * @param request the schema to set or update the AppSearch database to. - * @return a {@link ListenableFuture} which resolves to a {@link SetSchemaResponse} object. - */ - @NonNull - ListenableFuture setSchema(@NonNull SetSchemaRequest request); - - /** - * Retrieves the schema most recently successfully provided to {@link #setSchema}. - * - * @return The pending {@link GetSchemaResponse} of performing this operation. - */ - // This call hits disk; async API prevents us from treating these calls as properties. - @SuppressLint("KotlinPropertyAccess") - @NonNull - ListenableFuture getSchema(); - - /** - * Retrieves the set of all namespaces in the current database with at least one document. - * - * @return The pending result of performing this operation. - */ - @NonNull - ListenableFuture> getNamespaces(); - - /** - * Indexes documents into the {@link AppSearchSessionShim} database. - * - *

    Each {@link GenericDocument} object must have a {@code schemaType} field set to an {@link - * AppSearchSchema} type that has been previously registered by calling the {@link #setSchema} - * method. - * - * @param request containing documents to be indexed. - * @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The - * keys of the returned {@link AppSearchBatchResult} are the IDs of the input documents. The - * values are either {@code null} if the corresponding document was successfully indexed, or - * a failed {@link AppSearchResult} otherwise. - */ - @NonNull - ListenableFuture> put(@NonNull PutDocumentsRequest request); - - /** - * Gets {@link GenericDocument} objects by document IDs in a namespace from the {@link - * AppSearchSessionShim} database. - * - * @param request a request containing a namespace and IDs to get documents for. - * @return A {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The - * keys of the {@link AppSearchBatchResult} represent the input document IDs from the {@link - * GetByDocumentIdRequest} object. The values are either the corresponding {@link - * GenericDocument} object for the ID on success, or an {@link AppSearchResult} object on - * failure. For example, if an ID is not found, the value for that ID will be set to an - * {@link AppSearchResult} object with result code: {@link - * AppSearchResult#RESULT_NOT_FOUND}. - */ - @NonNull - ListenableFuture> getByDocumentId( - @NonNull GetByDocumentIdRequest request); - - /** - * Retrieves documents from the open {@link AppSearchSessionShim} that match a given query - * string and type of search provided. - * - *

    Query strings can be empty, contain one term with no operators, or contain multiple terms - * and operators. - * - *

    For query strings that are empty, all documents that match the {@link SearchSpec} will be - * returned. - * - *

    For query strings with a single term and no operators, documents that match the provided - * query string and {@link SearchSpec} will be returned. - * - *

    The following operators are supported: - * - *

      - *
    • AND (implicit) - *

      AND is an operator that matches documents that contain all provided terms. - *

      NOTE: A space between terms is treated as an "AND" operator. Explicitly - * including "AND" in a query string will treat "AND" as a term, returning documents that - * also contain "AND". - *

      Example: "apple AND banana" matches documents that contain the terms "apple", "and", - * "banana". - *

      Example: "apple banana" matches documents that contain both "apple" and "banana". - *

      Example: "apple banana cherry" matches documents that contain "apple", "banana", and - * "cherry". - *

    • OR - *

      OR is an operator that matches documents that contain any provided term. - *

      Example: "apple OR banana" matches documents that contain either "apple" or - * "banana". - *

      Example: "apple OR banana OR cherry" matches documents that contain any of "apple", - * "banana", or "cherry". - *

    • Exclusion (-) - *

      Exclusion (-) is an operator that matches documents that do not contain the - * provided term. - *

      Example: "-apple" matches documents that do not contain "apple". - *

    • Grouped Terms - *

      For queries that require multiple operators and terms, terms can be grouped into - * subqueries. Subqueries are contained within an open "(" and close ")" parenthesis. - *

      Example: "(donut OR bagel) (coffee OR tea)" matches documents that contain either - * "donut" or "bagel" and either "coffee" or "tea". - *

    • Property Restricts - *

      For queries that require a term to match a specific {@link AppSearchSchema} property - * of a document, a ":" must be included between the property name and the term. - *

      Example: "subject:important" matches documents that contain the term "important" in - * the "subject" property. - *

    - * - *

    Additional search specifications, such as filtering by {@link AppSearchSchema} type or - * adding projection, can be set by calling the corresponding {@link SearchSpec.Builder} setter. - * - *

    This method is lightweight. The heavy work will be done in {@link - * SearchResultsShim#getNextPage}. - * - * @param queryExpression query string to search. - * @param searchSpec spec for setting document filters, adding projection, setting term match - * type, etc. - * @return a {@link SearchResultsShim} object for retrieved matched documents. - */ - @NonNull - SearchResultsShim search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec); - - /** - * Reports usage of a particular document by namespace and ID. - * - *

    A usage report represents an event in which a user interacted with or viewed a document. - * - *

    For each call to {@link #reportUsage}, AppSearch updates usage count and usage recency - * metrics for that particular document. These metrics are used for ordering {@link #search} - * results by the {@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} and {@link - * SearchSpec#RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP} ranking strategies. - * - *

    Reporting usage of a document is optional. - * - * @param request The usage reporting request. - * @return The pending result of performing this operation which resolves to {@code null} on - * success. - */ - @NonNull - ListenableFuture reportUsage(@NonNull ReportUsageRequest request); - - /** - * Removes {@link GenericDocument} objects by document IDs in a namespace from the {@link - * AppSearchSessionShim} database. - * - *

    Removed documents will no longer be surfaced by {@link #search} or {@link - * #getByDocumentId} calls. - * - *

    Once the database crosses the document count or byte usage threshold, removed documents - * will be deleted from disk. - * - * @param request {@link RemoveByDocumentIdRequest} with IDs in a namespace to remove from the - * index. - * @return a {@link ListenableFuture} which resolves to an {@link AppSearchBatchResult}. The - * keys of the {@link AppSearchBatchResult} represent the input IDs from the {@link - * RemoveByDocumentIdRequest} object. The values are either {@code null} on success, or a - * failed {@link AppSearchResult} otherwise. IDs that are not found will return a failed - * {@link AppSearchResult} with a result code of {@link AppSearchResult#RESULT_NOT_FOUND}. - */ - @NonNull - ListenableFuture> remove( - @NonNull RemoveByDocumentIdRequest request); - - /** - * Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they - * match the {@code queryExpression} in given namespaces and schemaTypes which is set via {@link - * SearchSpec.Builder#addFilterNamespaces} and {@link SearchSpec.Builder#addFilterSchemas}. - * - *

    An empty {@code queryExpression} matches all documents. - * - *

    An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in the - * current database. - * - * @param queryExpression Query String to search. - * @param searchSpec Spec containing schemaTypes, namespaces and query expression indicates how - * document will be removed. All specific about how to scoring, ordering, snippeting and - * resulting will be ignored. - * @return The pending result of performing this operation. - */ - @NonNull - ListenableFuture remove(@NonNull String queryExpression, @NonNull SearchSpec searchSpec); - - /** - * Gets the storage info for this {@link AppSearchSessionShim} database. - * - *

    This may take time proportional to the number of documents and may be inefficient to call - * repeatedly. - * - * @return a {@link ListenableFuture} which resolves to a {@link StorageInfo} object. - */ - @NonNull - ListenableFuture getStorageInfo(); - - /** - * Flush all schema and document updates, additions, and deletes to disk if possible. - * - *

    The request is not guaranteed to be handled and may be ignored by some implementations of - * AppSearchSessionShim. - * - * @return The pending result of performing this operation. {@link - * android.app.appsearch.exceptions.AppSearchException} with {@link - * AppSearchResult#RESULT_INTERNAL_ERROR} will be set to the future if we hit error when - * save to disk. - */ - @NonNull - ListenableFuture requestFlush(); - - /** - * Closes the {@link AppSearchSessionShim} to persist all schema and document updates, - * additions, and deletes to disk. - */ - @Override - void close(); -} diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java deleted file mode 100644 index ec9a42eaa2769..0000000000000 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java +++ /dev/null @@ -1,94 +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 com.android.server.appsearch.testing; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; - -import android.app.appsearch.AppSearchBatchResult; -import android.app.appsearch.AppSearchSessionShim; -import android.app.appsearch.GenericDocument; -import android.app.appsearch.GetByDocumentIdRequest; -import android.app.appsearch.SearchResult; -import android.app.appsearch.SearchResultsShim; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.concurrent.Future; - -public class AppSearchTestUtils { - - public static AppSearchBatchResult checkIsBatchResultSuccess( - Future> future) throws Exception { - AppSearchBatchResult result = future.get(); - assertWithMessage("AppSearchBatchResult not successful: " + result) - .that(result.isSuccess()) - .isTrue(); - return result; - } - - public static List doGet( - AppSearchSessionShim session, String namespace, String... ids) throws Exception { - AppSearchBatchResult result = - checkIsBatchResultSuccess( - session.getByDocumentId( - new GetByDocumentIdRequest.Builder(namespace).addIds(ids).build())); - assertThat(result.getSuccesses()).hasSize(ids.length); - assertThat(result.getFailures()).isEmpty(); - List list = new ArrayList<>(ids.length); - for (String id : ids) { - list.add(result.getSuccesses().get(id)); - } - return list; - } - - public static List doGet( - AppSearchSessionShim session, GetByDocumentIdRequest request) throws Exception { - AppSearchBatchResult result = - checkIsBatchResultSuccess(session.getByDocumentId(request)); - Set ids = request.getIds(); - assertThat(result.getSuccesses()).hasSize(ids.size()); - assertThat(result.getFailures()).isEmpty(); - List list = new ArrayList<>(ids.size()); - for (String id : ids) { - list.add(result.getSuccesses().get(id)); - } - return list; - } - - public static List convertSearchResultsToDocuments( - SearchResultsShim searchResults) throws Exception { - List results = retrieveAllSearchResults(searchResults); - List documents = new ArrayList<>(results.size()); - for (SearchResult result : results) { - documents.add(result.getGenericDocument()); - } - return documents; - } - - public static List retrieveAllSearchResults(SearchResultsShim searchResults) - throws Exception { - List page = searchResults.getNextPage().get(); - List results = new ArrayList<>(); - while (!page.isEmpty()) { - results.addAll(page); - page = searchResults.getNextPage().get(); - } - return results; - } -} diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java deleted file mode 100644 index 892f84e5bdb88..0000000000000 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/GlobalSearchSessionShim.java +++ /dev/null @@ -1,82 +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 android.annotation.NonNull; - -import com.google.common.util.concurrent.ListenableFuture; - -import java.io.Closeable; - -/** - * Provides a connection to all AppSearch databases the querying application has been granted access - * to. - * - *

    All implementations of this interface must be thread safe. - * - * @see AppSearchSessionShim - */ -public interface GlobalSearchSessionShim extends Closeable { - /** - * Retrieves documents from all AppSearch databases that the querying application has access to. - * - *

    Applications can be granted access to documents by specifying {@link - * SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage}, or {@link - * SetSchemaRequest.Builder#setDocumentClassVisibilityForPackage} when building a schema. - * - *

    Document access can also be granted to system UIs by specifying {@link - * SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem}, or {@link - * SetSchemaRequest.Builder#setDocumentClassDisplayedBySystem} when building a schema. - * - *

    See {@link AppSearchSessionShim#search} for a detailed explanation on forming a query - * string. - * - *

    This method is lightweight. The heavy work will be done in {@link - * SearchResultsShim#getNextPage}. - * - * @param queryExpression query string to search. - * @param searchSpec spec for setting document filters, adding projection, setting term match - * type, etc. - * @return a {@link SearchResultsShim} object for retrieved matched documents. - */ - @NonNull - SearchResultsShim search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec); - - /** - * Reports that a particular document has been used from a system surface. - * - *

    See {@link AppSearchSessionShim#reportUsage} for a general description of document usage, - * as well as an API that can be used by the app itself. - * - *

    Usage reported via this method is accounted separately from usage reported via {@link - * AppSearchSessionShim#reportUsage} and may be accessed using the constants {@link - * SearchSpec#RANKING_STRATEGY_SYSTEM_USAGE_COUNT} and {@link - * SearchSpec#RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP}. - * - * @return The pending result of performing this operation which resolves to {@code null} on - * success. The pending result will be completed with an {@link - * android.app.appsearch.exceptions.AppSearchException} with a code of {@link - * AppSearchResult#RESULT_SECURITY_ERROR} if this API is invoked by an app which is not part - * of the system. - */ - @NonNull - ListenableFuture reportSystemUsage(@NonNull ReportSystemUsageRequest request); - - /** Closes the {@link GlobalSearchSessionShim}. */ - @Override - void close(); -} diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java deleted file mode 100644 index 38f61f83d24e0..0000000000000 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java +++ /dev/null @@ -1,56 +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 android.annotation.NonNull; - -import com.google.common.util.concurrent.ListenableFuture; - -import java.io.Closeable; -import java.util.List; - -/** - * Encapsulates results of a search operation. - * - *

    Each {@link AppSearchSessionShim#search} operation returns a list of {@link SearchResult} - * objects, referred to as a "page", limited by the size configured by {@link - * SearchSpec.Builder#setResultCountPerPage}. - * - *

    To fetch a page of results, call {@link #getNextPage()}. - * - *

    All instances of {@link SearchResultsShim} must call {@link SearchResultsShim#close()} after - * the results are fetched. - * - *

    This class is not thread safe. - */ -public interface SearchResultsShim extends Closeable { - /** - * Retrieves the next page of {@link SearchResult} objects. - * - *

    The page size is configured by {@link SearchSpec.Builder#setResultCountPerPage}. - * - *

    Continue calling this method to access results until it returns an empty list, signifying - * there are no more results. - * - * @return a {@link ListenableFuture} which resolves to a list of {@link SearchResult} objects. - */ - @NonNull - ListenableFuture> getNextPage(); - - @Override - void close(); -} diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/package-info.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/package-info.java deleted file mode 100644 index 5c919b4104556..0000000000000 --- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/package-info.java +++ /dev/null @@ -1,20 +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. - */ - -/** - * @hide - */ -package com.android.server.appsearch.testing; diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java b/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java deleted file mode 100644 index 07e4333e74b18..0000000000000 --- a/core/tests/coretests/src/android/app/appsearch/AppSearchSessionUnitTest.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * 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 android.app.appsearch; - -import static android.app.appsearch.SearchSpec.TERM_MATCH_PREFIX; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.expectThrows; - -import android.app.appsearch.exceptions.AppSearchException; -import android.content.Context; - -import androidx.test.core.app.ApplicationProvider; - -import com.android.server.appsearch.testing.AppSearchEmail; - -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; - -public class AppSearchSessionUnitTest { - private final Context mContext = ApplicationProvider.getApplicationContext(); - private final AppSearchManager mAppSearch = mContext.getSystemService(AppSearchManager.class); - private final Executor mExecutor = mContext.getMainExecutor(); - private AppSearchSession mSearchSession; - - @Before - public void setUp() throws Exception { - // Remove all documents from any instances that may have been created in the tests. - Objects.requireNonNull(mAppSearch); - AppSearchManager.SearchContext searchContext = - new AppSearchManager.SearchContext.Builder("testDb").build(); - CompletableFuture> future = new CompletableFuture<>(); - mAppSearch.createSearchSession(searchContext, mExecutor, future::complete); - mSearchSession = future.get().getResultValue(); - - CompletableFuture> schemaFuture = - new CompletableFuture<>(); - mSearchSession.setSchema( - new SetSchemaRequest.Builder().setForceOverride(true).build(), mExecutor, mExecutor, - schemaFuture::complete); - - schemaFuture.get().getResultValue(); - } - - @Test - public void testPutDocument_throwsNullException() throws Exception { - // Create a document - AppSearchEmail inEmail = - new AppSearchEmail.Builder("namespace", "uri1") - .setFrom("from@example.com") - .setTo("to1@example.com", "to2@example.com") - .setSubject("testPut example") - .setBody("This is the body of the testPut email") - .build(); - - // clear the document bundle to make our service crash and throw an NullPointerException. - inEmail.getBundle().clear(); - CompletableFuture> putDocumentsFuture = - new CompletableFuture<>(); - - // Index the broken document. - mSearchSession.put( - new PutDocumentsRequest.Builder().addGenericDocuments(inEmail).build(), - mExecutor, new BatchResultCallback() { - @Override - public void onResult(AppSearchBatchResult result) { - putDocumentsFuture.complete(result); - } - - @Override - public void onSystemError(Throwable throwable) { - putDocumentsFuture.completeExceptionally(throwable); - } - }); - - // Verify the NullPointException has been thrown. - ExecutionException executionException = - expectThrows(ExecutionException.class, putDocumentsFuture::get); - assertThat(executionException.getCause()).isInstanceOf(AppSearchException.class); - AppSearchException appSearchException = (AppSearchException) executionException.getCause(); - assertThat(appSearchException.getResultCode()) - .isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR); - assertThat(appSearchException.getMessage()).startsWith("NullPointerException"); - } - - @Test - public void testGetEmptyNextPage() throws Exception { - // Set the schema. - CompletableFuture> schemaFuture = - new CompletableFuture<>(); - mSearchSession.setSchema( - new SetSchemaRequest.Builder() - .addSchemas(new AppSearchSchema.Builder("schema1").build()) - .setForceOverride(true).build(), - mExecutor, mExecutor, schemaFuture::complete); - schemaFuture.get().getResultValue(); - - // Create a document and index it. - GenericDocument document1 = new GenericDocument.Builder<>("namespace", "id1", - "schema1").build(); - CompletableFuture> putDocumentsFuture = - new CompletableFuture<>(); - mSearchSession.put( - new PutDocumentsRequest.Builder().addGenericDocuments(document1).build(), - mExecutor, new BatchResultCallback() { - @Override - public void onResult(AppSearchBatchResult result) { - putDocumentsFuture.complete(result); - } - - @Override - public void onSystemError(Throwable throwable) { - putDocumentsFuture.completeExceptionally(throwable); - } - }); - putDocumentsFuture.get(); - - // Search and get the first page. - SearchSpec searchSpec = new SearchSpec.Builder() - .setTermMatch(TERM_MATCH_PREFIX) - .setResultCountPerPage(1) - .build(); - SearchResults searchResults = mSearchSession.search("", searchSpec); - - CompletableFuture>> getNextPageFuture = - new CompletableFuture<>(); - searchResults.getNextPage(mExecutor, getNextPageFuture::complete); - List results = getNextPageFuture.get().getResultValue(); - assertThat(results).hasSize(1); - assertThat(results.get(0).getGenericDocument()).isEqualTo(document1); - - // We get all documents, and it shouldn't fail if we keep calling getNextPage(). - getNextPageFuture = new CompletableFuture<>(); - searchResults.getNextPage(mExecutor, getNextPageFuture::complete); - results = getNextPageFuture.get().getResultValue(); - assertThat(results).isEmpty(); - } - - @Test - public void testGetEmptyNextPage_multiPages() throws Exception { - // Set the schema. - CompletableFuture> schemaFuture = - new CompletableFuture<>(); - mSearchSession.setSchema( - new SetSchemaRequest.Builder() - .addSchemas(new AppSearchSchema.Builder("schema1").build()) - .setForceOverride(true).build(), - mExecutor, mExecutor, schemaFuture::complete); - schemaFuture.get().getResultValue(); - - // Create a document and insert 3 package1 documents - GenericDocument document1 = new GenericDocument.Builder<>("namespace", "id1", - "schema1").build(); - GenericDocument document2 = new GenericDocument.Builder<>("namespace", "id2", - "schema1").build(); - GenericDocument document3 = new GenericDocument.Builder<>("namespace", "id3", - "schema1").build(); - CompletableFuture> putDocumentsFuture = - new CompletableFuture<>(); - mSearchSession.put( - new PutDocumentsRequest.Builder() - .addGenericDocuments(document1, document2, document3).build(), - mExecutor, new BatchResultCallback() { - @Override - public void onResult(AppSearchBatchResult result) { - putDocumentsFuture.complete(result); - } - - @Override - public void onSystemError(Throwable throwable) { - putDocumentsFuture.completeExceptionally(throwable); - } - }); - putDocumentsFuture.get(); - - // Search for only 2 result per page - SearchSpec searchSpec = new SearchSpec.Builder() - .setTermMatch(TERM_MATCH_PREFIX) - .setResultCountPerPage(2) - .build(); - SearchResults searchResults = mSearchSession.search("", searchSpec); - - // Get the first page, it contains 2 results. - List outDocs = new ArrayList<>(); - CompletableFuture>> getNextPageFuture = - new CompletableFuture<>(); - searchResults.getNextPage(mExecutor, getNextPageFuture::complete); - List results = getNextPageFuture.get().getResultValue(); - assertThat(results).hasSize(2); - outDocs.add(results.get(0).getGenericDocument()); - outDocs.add(results.get(1).getGenericDocument()); - - // Get the second page, it contains only 1 result. - getNextPageFuture = new CompletableFuture<>(); - searchResults.getNextPage(mExecutor, getNextPageFuture::complete); - results = getNextPageFuture.get().getResultValue(); - assertThat(results).hasSize(1); - outDocs.add(results.get(0).getGenericDocument()); - - assertThat(outDocs).containsExactly(document1, document2, document3); - - // We get all documents, and it shouldn't fail if we keep calling getNextPage(). - getNextPageFuture = new CompletableFuture<>(); - searchResults.getNextPage(mExecutor, getNextPageFuture::complete); - results = getNextPageFuture.get().getResultValue(); - assertThat(results).isEmpty(); - } -} diff --git a/core/tests/coretests/src/android/app/appsearch/OWNERS b/core/tests/coretests/src/android/app/appsearch/OWNERS deleted file mode 100644 index 24f6b0b6b2b61..0000000000000 --- a/core/tests/coretests/src/android/app/appsearch/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /apex/appsearch/OWNERS \ No newline at end of file diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java deleted file mode 100644 index 29b0228c93b3d..0000000000000 --- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java +++ /dev/null @@ -1,56 +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 com.android.server.appsearch.testing.AppSearchEmail; - -import org.junit.Test; - -public class AppSearchEmailTest { - - @Test - public void testBuildEmailAndGetValue() { - AppSearchEmail email = - new AppSearchEmail.Builder("namespace", "id") - .setFrom("FakeFromAddress") - .setCc("CC1", "CC2") - // Score and Property are mixed into the middle to make sure - // DocumentBuilder's - // methods can be interleaved with EmailBuilder's methods. - .setScore(1) - .setPropertyString("propertyKey", "propertyValue1", "propertyValue2") - .setSubject("subject") - .setBody("EmailBody") - .build(); - - assertThat(email.getNamespace()).isEqualTo("namespace"); - assertThat(email.getId()).isEqualTo("id"); - assertThat(email.getFrom()).isEqualTo("FakeFromAddress"); - assertThat(email.getTo()).isNull(); - assertThat(email.getCc()).asList().containsExactly("CC1", "CC2"); - assertThat(email.getBcc()).isNull(); - assertThat(email.getScore()).isEqualTo(1); - assertThat(email.getPropertyString("propertyKey")).isEqualTo("propertyValue1"); - assertThat(email.getPropertyStringArray("propertyKey")) - .asList() - .containsExactly("propertyValue1", "propertyValue2"); - assertThat(email.getSubject()).isEqualTo("subject"); - assertThat(email.getBody()).isEqualTo("EmailBody"); - } -} diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java deleted file mode 100644 index 228a061ae3c8b..0000000000000 --- a/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchResultTest.java +++ /dev/null @@ -1,41 +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.junit.Assert.assertThrows; - -import org.junit.Test; - -public class AppSearchResultTest { - @Test - public void testMapNullPointerException() { - NullPointerException e = - assertThrows( - NullPointerException.class, - () -> { - Object o = null; - o.toString(); - }); - AppSearchResult result = AppSearchResult.throwableToFailedResult(e); - assertThat(result.getResultCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR); - // Makes sure the exception name is included in the string. Some exceptions have terse or - // missing strings so it's confusing to read the output without the exception name. - assertThat(result.getErrorMessage()).startsWith("NullPointerException"); - } -} diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java deleted file mode 100644 index 3d820acf2d221..0000000000000 --- a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java +++ /dev/null @@ -1,79 +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 android.os.Bundle; -import android.os.Parcel; - -import org.junit.Test; - -public class GenericDocumentTest { - @Test - public void testRecreateFromParcel() { - GenericDocument inDoc = - new GenericDocument.Builder<>("namespace", "id1", "schema1") - .setScore(42) - .setPropertyString("propString", "Hello") - .setPropertyBytes("propBytes", new byte[][] {{1, 2}}) - .setPropertyDocument( - "propDocument", - new GenericDocument.Builder<>("namespace", "id2", "schema2") - .setPropertyString("propString", "Goodbye") - .setPropertyBytes("propBytes", new byte[][] {{3, 4}}) - .build()) - .build(); - - // Serialize the document - Parcel inParcel = Parcel.obtain(); - inParcel.writeBundle(inDoc.getBundle()); - byte[] data = inParcel.marshall(); - inParcel.recycle(); - - // Deserialize the document - Parcel outParcel = Parcel.obtain(); - outParcel.unmarshall(data, 0, data.length); - outParcel.setDataPosition(0); - Bundle outBundle = outParcel.readBundle(); - outParcel.recycle(); - - // Compare results - GenericDocument outDoc = new GenericDocument(outBundle); - assertThat(inDoc).isEqualTo(outDoc); - assertThat(outDoc.getPropertyString("propString")).isEqualTo("Hello"); - assertThat(outDoc.getPropertyBytesArray("propBytes")).isEqualTo(new byte[][] {{1, 2}}); - assertThat(outDoc.getPropertyDocument("propDocument").getPropertyString("propString")) - .isEqualTo("Goodbye"); - assertThat(outDoc.getPropertyDocument("propDocument").getPropertyBytesArray("propBytes")) - .isEqualTo(new byte[][] {{3, 4}}); - } - - @Test - public void testPutLargeDocument_exceedLimit() throws Exception { - // Create a String property that has a very large property. - char[] chars = new char[10_000_000]; - String property = new StringBuilder().append(chars).append("the end.").toString(); - - GenericDocument doc = - new GenericDocument.Builder<>("namespace", "id1", "schema1") - .setPropertyString("propString", property) - .build(); - - assertThat(doc.getPropertyString("propString")).isEqualTo(property); - } -} diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java deleted file mode 100644 index a613e7774d33b..0000000000000 --- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java +++ /dev/null @@ -1,60 +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 android.os.Bundle; - -import org.junit.Test; - -public class SearchSpecTest { - - @Test - public void testGetBundle() { - SearchSpec searchSpec = - new SearchSpec.Builder() - .setTermMatch(SearchSpec.TERM_MATCH_PREFIX) - .addFilterNamespaces("namespace1", "namespace2") - .addFilterSchemas("schemaTypes1", "schemaTypes2") - .addFilterPackageNames("package1", "package2") - .setSnippetCount(5) - .setSnippetCountPerProperty(10) - .setMaxSnippetSize(15) - .setResultCountPerPage(42) - .setOrder(SearchSpec.ORDER_ASCENDING) - .setRankingStrategy(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE) - .build(); - - Bundle bundle = searchSpec.getBundle(); - assertThat(bundle.getInt(SearchSpec.TERM_MATCH_TYPE_FIELD)) - .isEqualTo(SearchSpec.TERM_MATCH_PREFIX); - assertThat(bundle.getStringArrayList(SearchSpec.NAMESPACE_FIELD)) - .containsExactly("namespace1", "namespace2"); - assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_FIELD)) - .containsExactly("schemaTypes1", "schemaTypes2"); - assertThat(bundle.getStringArrayList(SearchSpec.PACKAGE_NAME_FIELD)) - .containsExactly("package1", "package2"); - assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_FIELD)).isEqualTo(5); - assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_PER_PROPERTY_FIELD)).isEqualTo(10); - assertThat(bundle.getInt(SearchSpec.MAX_SNIPPET_FIELD)).isEqualTo(15); - assertThat(bundle.getInt(SearchSpec.NUM_PER_PAGE_FIELD)).isEqualTo(42); - assertThat(bundle.getInt(SearchSpec.ORDER_FIELD)).isEqualTo(SearchSpec.ORDER_ASCENDING); - assertThat(bundle.getInt(SearchSpec.RANKING_STRATEGY_FIELD)) - .isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE); - } -} diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaResponseTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaResponseTest.java deleted file mode 100644 index 0bbea42b3ee88..0000000000000 --- a/core/tests/coretests/src/android/app/appsearch/external/app/SetSchemaResponseTest.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch; - -import static com.google.common.truth.Truth.assertThat; - -import org.junit.Test; - -public class SetSchemaResponseTest { - @Test - public void testRebuild() { - SetSchemaResponse.MigrationFailure failure1 = - new SetSchemaResponse.MigrationFailure( - "namespace", - "failure1", - "schemaType", - AppSearchResult.newFailedResult( - AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage")); - SetSchemaResponse.MigrationFailure failure2 = - new SetSchemaResponse.MigrationFailure( - "namespace", - "failure2", - "schemaType", - AppSearchResult.newFailedResult( - AppSearchResult.RESULT_INTERNAL_ERROR, "errorMessage")); - - SetSchemaResponse original = - new SetSchemaResponse.Builder() - .addDeletedType("delete1") - .addIncompatibleType("incompatible1") - .addMigratedType("migrated1") - .addMigrationFailure(failure1) - .build(); - assertThat(original.getDeletedTypes()).containsExactly("delete1"); - assertThat(original.getIncompatibleTypes()).containsExactly("incompatible1"); - assertThat(original.getMigratedTypes()).containsExactly("migrated1"); - assertThat(original.getMigrationFailures()).containsExactly(failure1); - - SetSchemaResponse rebuild = - original.toBuilder() - .addDeletedType("delete2") - .addIncompatibleType("incompatible2") - .addMigratedType("migrated2") - .addMigrationFailure(failure2) - .build(); - - // rebuild won't effect the original object - assertThat(original.getDeletedTypes()).containsExactly("delete1"); - assertThat(original.getIncompatibleTypes()).containsExactly("incompatible1"); - assertThat(original.getMigratedTypes()).containsExactly("migrated1"); - assertThat(original.getMigrationFailures()).containsExactly(failure1); - - assertThat(rebuild.getDeletedTypes()).containsExactly("delete1", "delete2"); - assertThat(rebuild.getIncompatibleTypes()) - .containsExactly("incompatible1", "incompatible2"); - assertThat(rebuild.getMigratedTypes()).containsExactly("migrated1", "migrated2"); - assertThat(rebuild.getMigrationFailures()).containsExactly(failure1, failure2); - } -} diff --git a/core/tests/coretests/src/android/app/appsearch/external/exceptions/IllegalSchemaExceptionTest.java b/core/tests/coretests/src/android/app/appsearch/external/exceptions/IllegalSchemaExceptionTest.java deleted file mode 100644 index 1774d72c110a8..0000000000000 --- a/core/tests/coretests/src/android/app/appsearch/external/exceptions/IllegalSchemaExceptionTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch.exceptions; - -import static com.google.common.truth.Truth.assertThat; - -import org.junit.Test; - -public class IllegalSchemaExceptionTest { - @Test - public void testExceptionWithMessage() { - IllegalSchemaException e = new IllegalSchemaException("ERROR MESSAGE"); - assertThat(e.getMessage()).isEqualTo("ERROR MESSAGE"); - assertThat(e).isInstanceOf(IllegalArgumentException.class); - } -} diff --git a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java b/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java deleted file mode 100644 index 680ce52be4d64..0000000000000 --- a/core/tests/coretests/src/android/app/appsearch/external/util/BundleUtilTest.java +++ /dev/null @@ -1,277 +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.util; - -import static com.google.common.truth.Truth.assertThat; - -import android.os.Build; -import android.os.Bundle; -import android.os.ParcelUuid; -import android.os.Parcelable; -import android.util.Size; -import android.util.SizeF; -import android.util.SparseArray; - -import com.google.common.collect.ImmutableList; - -import org.junit.Test; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.UUID; - -public class BundleUtilTest { - @Test - public void testDeepEquals_self() { - Bundle one = new Bundle(); - one.putString("a", "a"); - assertThat(BundleUtil.deepEquals(one, one)).isTrue(); - } - - @Test - public void testDeepEquals_simple() { - Bundle one = new Bundle(); - one.putString("a", "a"); - - Bundle two = new Bundle(); - two.putString("a", "a"); - - assertThat(one).isNotEqualTo(two); - assertThat(BundleUtil.deepEquals(one, two)).isTrue(); - } - - @Test - public void testDeepEquals_keyMismatch() { - Bundle one = new Bundle(); - one.putString("a", "a"); - - Bundle two = new Bundle(); - two.putString("a", "a"); - two.putString("b", "b"); - assertThat(BundleUtil.deepEquals(one, two)).isFalse(); - } - - @Test - public void testDeepEquals_thorough_equal() { - Bundle[] inputs = new Bundle[2]; - for (int i = 0; i < 2; i++) { - inputs[i] = createThoroughBundle(); - } - assertThat(inputs[0]).isNotEqualTo(inputs[1]); - assertThat(BundleUtil.deepEquals(inputs[0], inputs[1])).isTrue(); - } - - @Test - public void testDeepEquals_thorough_notEqual() { - Bundle[] inputs = new Bundle[2]; - for (int i = 0; i < 2; i++) { - Bundle b = createThoroughBundle(); - // Create a difference - assertThat(b.containsKey("doubleArray")).isTrue(); - b.putDoubleArray("doubleArray", new double[] {18., i}); - inputs[i] = b; - } - assertThat(inputs[0]).isNotEqualTo(inputs[1]); - assertThat(BundleUtil.deepEquals(inputs[0], inputs[1])).isFalse(); - } - - @Test - public void testDeepEquals_nestedNotEquals() { - Bundle one = new Bundle(); - one.putString("a", "a"); - Bundle two = new Bundle(); - two.putBundle("b", one); - Bundle twoClone = new Bundle(); - twoClone.putBundle("b", one); - Bundle three = new Bundle(); - three.putBundle("b", two); - - ArrayList listOne = new ArrayList<>(ImmutableList.of(one, two, three)); - ArrayList listOneClone = new ArrayList<>(ImmutableList.of(one, twoClone, three)); - ArrayList listTwo = new ArrayList<>(ImmutableList.of(one, three, two)); - Bundle b1 = new Bundle(); - b1.putParcelableArrayList("key", listOne); - Bundle b1Clone = new Bundle(); - b1Clone.putParcelableArrayList("key", listOneClone); - Bundle b2 = new Bundle(); - b2.putParcelableArrayList("key", listTwo); - - assertThat(b1).isNotEqualTo(b1Clone); - assertThat(BundleUtil.deepEquals(b1, b1Clone)).isTrue(); - assertThat(BundleUtil.deepEquals(b1, b2)).isFalse(); - assertThat(BundleUtil.deepEquals(b1Clone, b2)).isFalse(); - } - - @Test - public void testDeepEquals_sparseArray() { - Parcelable parcelable1 = new ParcelUuid(UUID.randomUUID()); - Parcelable parcelable2 = new ParcelUuid(UUID.randomUUID()); - Parcelable parcelable3 = new ParcelUuid(UUID.randomUUID()); - - SparseArray array1 = new SparseArray<>(); - array1.put(1, parcelable1); - array1.put(10, parcelable2); - - SparseArray array1Clone = new SparseArray<>(); - array1Clone.put(1, parcelable1); - array1Clone.put(10, parcelable2); - - SparseArray array2 = new SparseArray<>(); - array2.put(1, parcelable1); - array2.put(10, parcelable3); // Different - - Bundle b1 = new Bundle(); - b1.putSparseParcelableArray("array1", array1); - Bundle b1Clone = new Bundle(); - b1Clone.putSparseParcelableArray("array1", array1Clone); - Bundle b2 = new Bundle(); - b2.putSparseParcelableArray("array1", array2); - - assertThat(b1).isNotEqualTo(b1Clone); - assertThat(BundleUtil.deepEquals(b1, b1Clone)).isTrue(); - assertThat(BundleUtil.deepEquals(b1, b2)).isFalse(); - assertThat(BundleUtil.deepEquals(b1Clone, b2)).isFalse(); - } - - @Test - public void testDeepHashCode_same() { - Bundle[] inputs = new Bundle[2]; - for (int i = 0; i < 2; i++) { - inputs[i] = createThoroughBundle(); - } - assertThat(BundleUtil.deepHashCode(inputs[0])) - .isEqualTo(BundleUtil.deepHashCode(inputs[1])); - } - - @Test - public void testDeepHashCode_different() { - Bundle[] inputs = new Bundle[2]; - for (int i = 0; i < 2; i++) { - Bundle b = createThoroughBundle(); - // Create a difference - assertThat(b.containsKey("doubleArray")).isTrue(); - b.putDoubleArray("doubleArray", new double[] {18., i}); - inputs[i] = b; - } - assertThat(BundleUtil.deepHashCode(inputs[0])) - .isNotEqualTo(BundleUtil.deepHashCode(inputs[1])); - } - - @Test - public void testHashCode_sparseArray() { - Parcelable parcelable1 = new ParcelUuid(UUID.randomUUID()); - Parcelable parcelable2 = new ParcelUuid(UUID.randomUUID()); - Parcelable parcelable3 = new ParcelUuid(UUID.randomUUID()); - - SparseArray array1 = new SparseArray<>(); - array1.put(1, parcelable1); - array1.put(10, parcelable2); - - SparseArray array1Clone = new SparseArray<>(); - array1Clone.put(1, parcelable1); - array1Clone.put(10, parcelable2); - - SparseArray array2 = new SparseArray<>(); - array2.put(1, parcelable1); - array2.put(10, parcelable3); // Different - - Bundle b1 = new Bundle(); - b1.putSparseParcelableArray("array1", array1); - Bundle b1Clone = new Bundle(); - b1Clone.putSparseParcelableArray("array1", array1Clone); - Bundle b2 = new Bundle(); - b2.putSparseParcelableArray("array1", array2); - - assertThat(b1.hashCode()).isNotEqualTo(b1Clone.hashCode()); - assertThat(BundleUtil.deepHashCode(b1)).isEqualTo(BundleUtil.deepHashCode(b1Clone)); - assertThat(BundleUtil.deepHashCode(b1)).isNotEqualTo(BundleUtil.deepHashCode(b2)); - } - - @Test - public void testDeepHashCode_differentKeys() { - Bundle[] inputs = new Bundle[2]; - for (int i = 0; i < 2; i++) { - Bundle b = new Bundle(); - b.putString("key" + i, "value"); - inputs[i] = b; - } - assertThat(BundleUtil.deepHashCode(inputs[0])) - .isNotEqualTo(BundleUtil.deepHashCode(inputs[1])); - } - - @Test - public void testDeepCopy() { - Bundle input = createThoroughBundle(); - Bundle output = BundleUtil.deepCopy(input); - assertThat(input).isNotSameInstanceAs(output); - assertThat(BundleUtil.deepEquals(input, output)).isTrue(); - - output.getIntegerArrayList("integerArrayList").add(5); - assertThat(BundleUtil.deepEquals(input, output)).isFalse(); - } - - private static Bundle createThoroughBundle() { - Bundle toy1 = new Bundle(); - toy1.putString("a", "a"); - Bundle toy2 = new Bundle(); - toy2.putInt("b", 2); - - Bundle b = new Bundle(); - // BaseBundle stuff - b.putBoolean("boolean", true); - b.putByte("byte", (byte) 1); - b.putChar("char", 'a'); - b.putShort("short", (short) 2); - b.putInt("int", 3); - b.putLong("long", 4L); - b.putFloat("float", 5f); - b.putDouble("double", 6f); - b.putString("string", "b"); - b.putCharSequence("charSequence", "c"); - b.putIntegerArrayList("integerArrayList", new ArrayList<>(ImmutableList.of(7, 8))); - b.putStringArrayList("stringArrayList", new ArrayList<>(ImmutableList.of("d", "e"))); - b.putCharSequenceArrayList( - "charSequenceArrayList", new ArrayList<>(ImmutableList.of("f", "g"))); - b.putSerializable("serializable", new BigDecimal(9)); - b.putBooleanArray("booleanArray", new boolean[] {true, false, true}); - b.putByteArray("byteArray", new byte[] {(byte) 10, (byte) 11}); - b.putShortArray("shortArray", new short[] {(short) 12, (short) 13}); - b.putCharArray("charArray", new char[] {'h', 'i'}); - b.putLongArray("longArray", new long[] {14L, 15L}); - b.putFloatArray("floatArray", new float[] {16f, 17f}); - b.putDoubleArray("doubleArray", new double[] {18., 19.}); - b.putStringArray("stringArray", new String[] {"j", "k"}); - b.putCharSequenceArray("charSequenceArrayList", new CharSequence[] {"l", "m"}); - - // Bundle stuff - b.putParcelable("parcelable", toy1); - if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) { - b.putSize("size", new Size(20, 21)); - b.putSizeF("sizeF", new SizeF(22f, 23f)); - } - b.putParcelableArray("parcelableArray", new Parcelable[] {toy1, toy2}); - b.putParcelableArrayList( - "parcelableArrayList", new ArrayList<>(ImmutableList.of(toy1, toy2))); - SparseArray sparseArray = new SparseArray<>(); - sparseArray.put(24, toy1); - sparseArray.put(1025, toy2); - b.putSparseParcelableArray("sparceParcelableArray", sparseArray); - b.putBundle("bundle", toy1); - - return b; - } -} diff --git a/core/tests/coretests/src/android/app/appsearch/external/util/IndentingStringBuilderTest.java b/core/tests/coretests/src/android/app/appsearch/external/util/IndentingStringBuilderTest.java deleted file mode 100644 index 057ecbd9dfe9f..0000000000000 --- a/core/tests/coretests/src/android/app/appsearch/external/util/IndentingStringBuilderTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch.util; - -import static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assert.assertThrows; - -import org.junit.Test; - -public class IndentingStringBuilderTest { - @Test - public void testAppendIndentedStrings() { - IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); - stringBuilder - .increaseIndentLevel() - .append("\nIndentLevel1\nIndentLevel1\n") - .decreaseIndentLevel() - .append("IndentLevel0,\n"); - - String str = stringBuilder.toString(); - String expectedString = "\n IndentLevel1\n IndentLevel1\nIndentLevel0,\n"; - - assertThat(str).isEqualTo(expectedString); - } - - @Test - public void testDecreaseIndentLevel_throwsException() { - IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); - - Exception e = - assertThrows( - IllegalStateException.class, () -> stringBuilder.decreaseIndentLevel()); - assertThat(e).hasMessageThat().contains("Cannot set indent level below 0."); - } - - @Test - public void testAppendIndentedObjects() { - IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); - Object stringProperty = "String"; - Object longProperty = 1L; - Object booleanProperty = true; - - stringBuilder - .append(stringProperty) - .append("\n") - .increaseIndentLevel() - .append(longProperty) - .append("\n") - .decreaseIndentLevel() - .append(booleanProperty); - - String str = stringBuilder.toString(); - String expectedString = "String\n 1\ntrue"; - - assertThat(str).isEqualTo(expectedString); - } - - @Test - public void testAppendIndentedStrings_doesNotIndentLineBreak() { - IndentingStringBuilder stringBuilder = new IndentingStringBuilder(); - - stringBuilder - .append("\n") - .increaseIndentLevel() - .append("\n\n") - .decreaseIndentLevel() - .append("\n"); - - String str = stringBuilder.toString(); - String expectedString = "\n\n\n\n"; - - assertThat(str).isEqualTo(expectedString); - } -} diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java deleted file mode 100644 index 9926953f110fa..0000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/appsearch/AppSearchConfigTest.java +++ /dev/null @@ -1,474 +0,0 @@ -/* - * 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.Assert; -import org.junit.Rule; -import org.junit.Test; - -/** - * Tests for {@link AppSearchConfig}. - * - *

    Build/Install/Run: atest FrameworksMockingServicesTests:AppSearchConfigTest - */ -public class AppSearchConfigTest { - @Rule - public final TestableDeviceConfig.TestableDeviceConfigRule - mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); - - @Test - public void testDefaultValues_allCachedValue() { - AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - - assertThat(appSearchConfig.getCachedMinTimeIntervalBetweenSamplesMillis()).isEqualTo( - AppSearchConfig.DEFAULT_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS); - assertThat(appSearchConfig.getCachedSamplingIntervalDefault()).isEqualTo( - AppSearchConfig.DEFAULT_SAMPLING_INTERVAL); - assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( - AppSearchConfig.DEFAULT_SAMPLING_INTERVAL); - assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo( - AppSearchConfig.DEFAULT_SAMPLING_INTERVAL); - assertThat(appSearchConfig.getCachedSamplingIntervalForInitializeStats()).isEqualTo( - AppSearchConfig.DEFAULT_SAMPLING_INTERVAL); - assertThat(appSearchConfig.getCachedSamplingIntervalForSearchStats()).isEqualTo( - AppSearchConfig.DEFAULT_SAMPLING_INTERVAL); - assertThat(appSearchConfig.getCachedSamplingIntervalForGlobalSearchStats()).isEqualTo( - AppSearchConfig.DEFAULT_SAMPLING_INTERVAL); - assertThat(appSearchConfig.getCachedSamplingIntervalForOptimizeStats()).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); - assertThat(appSearchConfig.getCachedBytesOptimizeThreshold()).isEqualTo( - AppSearchConfig.DEFAULT_BYTES_OPTIMIZE_THRESHOLD); - assertThat(appSearchConfig.getCachedTimeOptimizeThresholdMs()).isEqualTo( - AppSearchConfig.DEFAULT_TIME_OPTIMIZE_THRESHOLD_MILLIS); - assertThat(appSearchConfig.getCachedDocCountOptimizeThreshold()).isEqualTo( - AppSearchConfig.DEFAULT_DOC_COUNT_OPTIMIZE_THRESHOLD); - } - - @Test - public void testCustomizedValue_minTimeIntervalBetweenSamplesMillis() { - final long minTimeIntervalBetweenSamplesMillis = -1; - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - Long.toString(minTimeIntervalBetweenSamplesMillis), - false); - - AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - - assertThat(appSearchConfig.getCachedMinTimeIntervalBetweenSamplesMillis()).isEqualTo( - minTimeIntervalBetweenSamplesMillis); - } - - @Test - public void testCustomizedValueOverride_minTimeIntervalBetweenSamplesMillis() { - long minTimeIntervalBetweenSamplesMillis = -1; - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - Long.toString(minTimeIntervalBetweenSamplesMillis), - false); - AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - - minTimeIntervalBetweenSamplesMillis = -2; - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - Long.toString(minTimeIntervalBetweenSamplesMillis), - false); - - assertThat(appSearchConfig.getCachedMinTimeIntervalBetweenSamplesMillis()).isEqualTo( - minTimeIntervalBetweenSamplesMillis); - } - - @Test - public void testCustomizedValue_allSamplingIntervals() { - final int samplingIntervalDefault = -1; - final int samplingIntervalPutDocumentStats = -2; - final int samplingIntervalBatchCallStats = -3; - final int samplingIntervalInitializeStats = -4; - final int samplingIntervalSearchStats = -5; - final int samplingIntervalGlobalSearchStats = -6; - final int samplingIntervalOptimizeStats = -7; - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Integer.toString(samplingIntervalDefault), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, - Integer.toString(samplingIntervalPutDocumentStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, - Integer.toString(samplingIntervalBatchCallStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS, - Integer.toString(samplingIntervalInitializeStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS, - Integer.toString(samplingIntervalSearchStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS, - Integer.toString(samplingIntervalGlobalSearchStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS, - Integer.toString(samplingIntervalOptimizeStats), - false); - - AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - - assertThat(appSearchConfig.getCachedSamplingIntervalDefault()).isEqualTo( - samplingIntervalDefault); - assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo( - samplingIntervalPutDocumentStats); - assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( - samplingIntervalBatchCallStats); - assertThat(appSearchConfig.getCachedSamplingIntervalForInitializeStats()).isEqualTo( - samplingIntervalInitializeStats); - assertThat(appSearchConfig.getCachedSamplingIntervalForSearchStats()).isEqualTo( - samplingIntervalSearchStats); - assertThat(appSearchConfig.getCachedSamplingIntervalForGlobalSearchStats()).isEqualTo( - samplingIntervalGlobalSearchStats); - assertThat(appSearchConfig.getCachedSamplingIntervalForOptimizeStats()).isEqualTo( - samplingIntervalOptimizeStats); - } - - @Test - public void testCustomizedValueOverride_allSamplingIntervals() { - int samplingIntervalDefault = -1; - int samplingIntervalPutDocumentStats = -2; - int samplingIntervalBatchCallStats = -3; - int samplingIntervalInitializeStats = -4; - int samplingIntervalSearchStats = -5; - int samplingIntervalGlobalSearchStats = -6; - int samplingIntervalOptimizeStats = -7; - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Integer.toString(samplingIntervalDefault), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, - Integer.toString(samplingIntervalPutDocumentStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, - Integer.toString(samplingIntervalBatchCallStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS, - Integer.toString(samplingIntervalInitializeStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS, - Integer.toString(samplingIntervalSearchStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS, - Integer.toString(samplingIntervalGlobalSearchStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS, - Integer.toString(samplingIntervalOptimizeStats), - false); - AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - - // Overrides - samplingIntervalDefault = -4; - samplingIntervalPutDocumentStats = -5; - samplingIntervalBatchCallStats = -6; - samplingIntervalInitializeStats = -7; - samplingIntervalSearchStats = -8; - samplingIntervalGlobalSearchStats = -9; - samplingIntervalOptimizeStats = -10; - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Integer.toString(samplingIntervalDefault), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, - Integer.toString(samplingIntervalPutDocumentStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, - Integer.toString(samplingIntervalBatchCallStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_INITIALIZE_STATS, - Integer.toString(samplingIntervalInitializeStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_SEARCH_STATS, - Integer.toString(samplingIntervalSearchStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_GLOBAL_SEARCH_STATS, - Integer.toString(samplingIntervalGlobalSearchStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_OPTIMIZE_STATS, - Integer.toString(samplingIntervalOptimizeStats), - false); - - assertThat(appSearchConfig.getCachedSamplingIntervalDefault()).isEqualTo( - samplingIntervalDefault); - assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo( - samplingIntervalPutDocumentStats); - assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( - samplingIntervalBatchCallStats); - assertThat(appSearchConfig.getCachedSamplingIntervalForInitializeStats()).isEqualTo( - samplingIntervalInitializeStats); - assertThat(appSearchConfig.getCachedSamplingIntervalForSearchStats()).isEqualTo( - samplingIntervalSearchStats); - assertThat(appSearchConfig.getCachedSamplingIntervalForGlobalSearchStats()).isEqualTo( - samplingIntervalGlobalSearchStats); - assertThat(appSearchConfig.getCachedSamplingIntervalForOptimizeStats()).isEqualTo( - samplingIntervalOptimizeStats); - } - - /** - * Tests if we fall back to {@link AppSearchConfig#DEFAULT_SAMPLING_INTERVAL} if both default - * sampling interval and custom value are not set in DeviceConfig, and there is some other - * sampling interval set. - */ - @Test - public void testFallbackToDefaultSamplingValue_useHardCodedDefault() { - final int samplingIntervalPutDocumentStats = -1; - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, - Integer.toString(samplingIntervalPutDocumentStats), - false); - - AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - - assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo( - samplingIntervalPutDocumentStats); - assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( - AppSearchConfig.DEFAULT_SAMPLING_INTERVAL); - } - - // Tests if we fall back to configured default sampling interval if custom value is not set in - // DeviceConfig. - @Test - public void testFallbackDefaultSamplingValue_useConfiguredDefault() { - final int samplingIntervalPutDocumentStats = -1; - final int samplingIntervalDefault = -2; - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, - Integer.toString(samplingIntervalPutDocumentStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Integer.toString(samplingIntervalDefault), - false); - - AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - - assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo( - samplingIntervalPutDocumentStats); - assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( - samplingIntervalDefault); - } - - // Tests that cached values should reflect latest values in DeviceConfig. - @Test - public void testFallbackDefaultSamplingValue_defaultValueChanged() { - int samplingIntervalPutDocumentStats = -1; - int samplingIntervalDefault = -2; - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, - Integer.toString(samplingIntervalPutDocumentStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Integer.toString(samplingIntervalDefault), - false); - - AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - - // Sampling values changed. - samplingIntervalPutDocumentStats = -3; - samplingIntervalDefault = -4; - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, - Integer.toString(samplingIntervalPutDocumentStats), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Integer.toString(samplingIntervalDefault), - false); - - assertThat(appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()).isEqualTo( - samplingIntervalPutDocumentStats); - assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( - samplingIntervalDefault); - } - - // Tests default sampling interval won't affect custom sampling intervals if they are set. - @Test - public void testShouldNotFallBack_ifValueConfigured() { - int samplingIntervalDefault = -1; - int samplingIntervalBatchCallStats = -2; - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Integer.toString(samplingIntervalDefault), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, - Integer.toString(samplingIntervalBatchCallStats), - false); - - AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - - // Default sampling interval changed. - samplingIntervalDefault = -3; - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Integer.toString(samplingIntervalDefault), - false); - - assertThat(appSearchConfig.getCachedSamplingIntervalForBatchCallStats()).isEqualTo( - samplingIntervalBatchCallStats); - } - - @Test - public void testCustomizedValue_maxDocument() { - 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 testCustomizedValue_optimizeThreshold() { - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_BYTES_OPTIMIZE_THRESHOLD, - Integer.toString(147147), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS, - Integer.toString(258258), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_DOC_COUNT_OPTIMIZE_THRESHOLD, - Integer.toString(369369), - false); - - AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - - assertThat(appSearchConfig.getCachedBytesOptimizeThreshold()).isEqualTo(147147); - assertThat(appSearchConfig.getCachedTimeOptimizeThresholdMs()).isEqualTo(258258); - assertThat(appSearchConfig.getCachedDocCountOptimizeThreshold()).isEqualTo(369369); - } - - @Test - public void testCustomizedValueOverride_optimizeThreshold() { - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_BYTES_OPTIMIZE_THRESHOLD, - Integer.toString(147147), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS, - Integer.toString(258258), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_DOC_COUNT_OPTIMIZE_THRESHOLD, - Integer.toString(369369), - false); - - AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - - // Override - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_BYTES_OPTIMIZE_THRESHOLD, - Integer.toString(741741), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_TIME_OPTIMIZE_THRESHOLD_MILLIS, - Integer.toString(852852), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_DOC_COUNT_OPTIMIZE_THRESHOLD, - Integer.toString(963963), - false); - - assertThat(appSearchConfig.getCachedBytesOptimizeThreshold()).isEqualTo(741741); - assertThat(appSearchConfig.getCachedTimeOptimizeThresholdMs()).isEqualTo(852852); - assertThat(appSearchConfig.getCachedDocCountOptimizeThreshold()).isEqualTo(963963); - } - - @Test - public void testNotUsable_afterClose() { - AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - - appSearchConfig.close(); - - Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", - IllegalStateException.class, - () -> appSearchConfig.getCachedMinTimeIntervalBetweenSamplesMillis()); - Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", - IllegalStateException.class, - () -> appSearchConfig.getCachedSamplingIntervalDefault()); - Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", - IllegalStateException.class, - () -> appSearchConfig.getCachedSamplingIntervalForBatchCallStats()); - Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", - IllegalStateException.class, - () -> appSearchConfig.getCachedSamplingIntervalForPutDocumentStats()); - Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", - IllegalStateException.class, - () -> appSearchConfig.getCachedSamplingIntervalForInitializeStats()); - Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", - IllegalStateException.class, - () -> appSearchConfig.getCachedSamplingIntervalForSearchStats()); - Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", - IllegalStateException.class, - () -> appSearchConfig.getCachedSamplingIntervalForGlobalSearchStats()); - Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", - IllegalStateException.class, - () -> appSearchConfig.getCachedSamplingIntervalForOptimizeStats()); - Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", - IllegalStateException.class, - () -> appSearchConfig.getCachedBytesOptimizeThreshold()); - Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", - IllegalStateException.class, - () -> appSearchConfig.getCachedTimeOptimizeThresholdMs()); - Assert.assertThrows("Trying to use a closed AppSearchConfig instance.", - IllegalStateException.class, - () -> appSearchConfig.getCachedDocCountOptimizeThreshold()); - } -} diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/FrameworkLimitConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/FrameworkLimitConfigTest.java deleted file mode 100644 index 088ed277aa803..0000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/appsearch/FrameworkLimitConfigTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * 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}. - * - *

    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/mockingservicestests/src/com/android/server/appsearch/OWNERS b/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS deleted file mode 100644 index 24f6b0b6b2b61..0000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /apex/appsearch/OWNERS \ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java deleted file mode 100644 index 28fcaee79572f..0000000000000 --- a/services/tests/mockingservicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * 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.stats; - -import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; - -import static com.google.common.truth.Truth.assertThat; - -import android.os.SystemClock; -import android.provider.DeviceConfig; - -import androidx.test.core.app.ApplicationProvider; - -import com.android.server.appsearch.AppSearchConfig; -import com.android.server.appsearch.external.localstorage.stats.CallStats; -import com.android.server.testables.TestableDeviceConfig; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.junit.MockitoJUnitRunner; - -/** - * Tests covering the functionalities in {@link PlatformLogger} requiring overriding some flags - * in {@link DeviceConfig}. - * - *

    To add tests NOT rely on overriding the configs, please add them in - * the tests for {@link PlatformLogger} in servicetests. - */ -@RunWith(MockitoJUnitRunner.class) -public class PlatformLoggerTest { - private static final int TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS = 100; - private static final int TEST_DEFAULT_SAMPLING_INTERVAL = 10; - private static final String TEST_PACKAGE_NAME = "packageName"; - private AppSearchConfig mAppSearchConfig; - - @Rule - public final TestableDeviceConfig.TestableDeviceConfigRule - mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); - - @Before - public void setUp() throws Exception { - mAppSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - } - - @Test - public void testCreateExtraStatsLocked_samplingIntervalNotSet_returnsDefault() { - PlatformLogger logger = new PlatformLogger( - ApplicationProvider.getApplicationContext(), - mAppSearchConfig); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - Long.toString(TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Integer.toString(TEST_DEFAULT_SAMPLING_INTERVAL), - false); - - // Make sure default sampling interval is used if there is no config set. - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_UNKNOWN).mSamplingInterval).isEqualTo( - TEST_DEFAULT_SAMPLING_INTERVAL); - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_INITIALIZE).mSamplingInterval).isEqualTo( - TEST_DEFAULT_SAMPLING_INTERVAL); - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_SEARCH).mSamplingInterval).isEqualTo( - TEST_DEFAULT_SAMPLING_INTERVAL); - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_FLUSH).mSamplingInterval).isEqualTo( - TEST_DEFAULT_SAMPLING_INTERVAL); - } - - - @Test - public void testCreateExtraStatsLocked_samplingIntervalSet_returnsConfigured() { - int putDocumentSamplingInterval = 1; - int batchCallSamplingInterval = 2; - PlatformLogger logger = new PlatformLogger( - ApplicationProvider.getApplicationContext(), mAppSearchConfig); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - Long.toString(TEST_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Integer.toString(TEST_DEFAULT_SAMPLING_INTERVAL), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_PUT_DOCUMENT_STATS, - Integer.toString(putDocumentSamplingInterval), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_FOR_BATCH_CALL_STATS, - Integer.toString(batchCallSamplingInterval), - false); - - // The default sampling interval should be used if no sampling interval is - // provided for certain call type. - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_INITIALIZE).mSamplingInterval).isEqualTo( - TEST_DEFAULT_SAMPLING_INTERVAL); - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_FLUSH).mSamplingInterval).isEqualTo( - TEST_DEFAULT_SAMPLING_INTERVAL); - - // The configured sampling interval is used if sampling interval is available - // for certain call type. - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_PUT_DOCUMENT).mSamplingInterval).isEqualTo( - putDocumentSamplingInterval); - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_PUT_DOCUMENTS).mSamplingInterval).isEqualTo( - batchCallSamplingInterval); - assertThat(logger.createExtraStatsLocked(TEST_PACKAGE_NAME, - CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH).mSamplingInterval).isEqualTo( - batchCallSamplingInterval); - } - - @Test - public void testShouldLogForTypeLocked_trueWhenSampleIntervalIsOne() { - final String testPackageName = "packageName"; - PlatformLogger logger = new PlatformLogger( - ApplicationProvider.getApplicationContext(), - mAppSearchConfig); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Long.toString(1), - false); - - // Sample should always be logged for the first time if sampling is disabled(value is one). - assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue(); - assertThat(logger.createExtraStatsLocked(testPackageName, - CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0); - } - - @Test - public void testShouldLogForTypeLocked_falseWhenSampleIntervalIsNegative() { - final String testPackageName = "packageName"; - PlatformLogger logger = new PlatformLogger( - ApplicationProvider.getApplicationContext(), - mAppSearchConfig); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Long.toString(-1), - false); - - // Makes sure sample will be excluded due to sampling if sample interval is negative. - assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse(); - // Skipped count should be 0 since it doesn't pass the sampling. - assertThat(logger.createExtraStatsLocked(testPackageName, - CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0); - } - - @Test - public void testShouldLogForTypeLocked_falseWhenWithinCoolOffInterval() { - // Next sample won't be excluded due to sampling. - final int samplingInterval = 1; - // Next sample would guaranteed to be too close. - final int minTimeIntervalBetweenSamplesMillis = Integer.MAX_VALUE; - final String testPackageName = "packageName"; - PlatformLogger logger = new PlatformLogger( - ApplicationProvider.getApplicationContext(), - mAppSearchConfig); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Long.toString(samplingInterval), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - Long.toString(minTimeIntervalBetweenSamplesMillis), - false); - logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime()); - - // Makes sure sample will be excluded due to rate limiting if samples are too close. - assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isFalse(); - assertThat(logger.createExtraStatsLocked(testPackageName, - CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(1); - } - - @Test - public void testShouldLogForTypeLocked_trueWhenOutsideOfCoolOffInterval() { - // Next sample won't be excluded due to sampling. - final int samplingInterval = 1; - // Next sample would guaranteed to be included. - final int minTimeIntervalBetweenSamplesMillis = 0; - final String testPackageName = "packageName"; - PlatformLogger logger = new PlatformLogger( - ApplicationProvider.getApplicationContext(), - mAppSearchConfig); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_SAMPLING_INTERVAL_DEFAULT, - Long.toString(samplingInterval), - false); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_APPSEARCH, - AppSearchConfig.KEY_MIN_TIME_INTERVAL_BETWEEN_SAMPLES_MILLIS, - Long.toString(minTimeIntervalBetweenSamplesMillis), - false); - logger.setLastPushTimeMillisLocked(SystemClock.elapsedRealtime()); - - // Makes sure sample will be logged if it is not too close to previous sample. - assertThat(logger.shouldLogForTypeLocked(CallStats.CALL_TYPE_PUT_DOCUMENT)).isTrue(); - assertThat(logger.createExtraStatsLocked(testPackageName, - CallStats.CALL_TYPE_PUT_DOCUMENT).mSkippedSampleCount).isEqualTo(0); - } -} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java b/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java deleted file mode 100644 index 0d475c00569ea..0000000000000 --- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java +++ /dev/null @@ -1,454 +0,0 @@ -/* - * 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. - */ - -// TODO(b/169883602): This is purposely a different package from the path so that it can access -// AppSearchImpl's methods without having to make them public. This should be replaced by proper -// global query integration tests that can test AppSearchImpl-VisibilityStore integration logic. -package com.android.server.appsearch.external.localstorage; - -import static android.Manifest.permission.READ_GLOBAL_APP_SEARCH_DATA; -import static android.content.pm.PackageManager.PERMISSION_DENIED; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.app.appsearch.AppSearchSchema; -import android.app.appsearch.PackageIdentifier; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.pm.PackageManager; -import android.os.UserHandle; -import android.util.ArrayMap; - -import androidx.test.core.app.ApplicationProvider; - -import com.android.compatibility.common.util.SystemUtil; -import com.android.server.appsearch.external.localstorage.util.PrefixUtil; -import com.android.server.appsearch.visibilitystore.VisibilityStoreImpl; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.mockito.Mockito; - -import java.util.Collections; -import java.util.Map; - -/** This tests AppSearchImpl when it's running with a platform-backed VisibilityStore. */ -public class AppSearchImplPlatformTest { - /** - * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class. - */ - private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true; - - @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); - private final Map mMockPackageManagers = new ArrayMap<>(); - private Context mContext; - private AppSearchImpl mAppSearchImpl; - private VisibilityStoreImpl mVisibilityStore; - private int mGlobalQuerierUid; - - @Before - public void setUp() throws Exception { - Context context = ApplicationProvider.getApplicationContext(); - mContext = new ContextWrapper(context) { - @Override - public Context createContextAsUser(UserHandle user, int flags) { - return new ContextWrapper(super.createContextAsUser(user, flags)) { - @Override - public PackageManager getPackageManager() { - return getMockPackageManager(user); - } - }; - } - - @Override - public PackageManager getPackageManager() { - return createContextAsUser(getUser(), /*flags=*/ 0).getPackageManager(); - } - }; - - // Give ourselves global query permissions - mAppSearchImpl = AppSearchImpl.create( - mTemporaryFolder.newFolder(), - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - mVisibilityStore = VisibilityStoreImpl.create(mAppSearchImpl, mContext); - mGlobalQuerierUid = - mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0); - } - - @Test - public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception { - // Values for a "foo" client - String packageNameFoo = "packageFoo"; - byte[] sha256CertFoo = new byte[] {10}; - int uidFoo = 1; - - // Make sure foo package will pass package manager checks. - PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); - when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt())) - .thenReturn(uidFoo); - when(mockPackageManager.hasSigningCertificate( - packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256)) - .thenReturn(true); - - // Make sure we have global query privileges and "foo" doesn't - when(mockPackageManager.checkPermission( - READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName())) - .thenReturn(PERMISSION_GRANTED); - when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo)) - .thenReturn(PERMISSION_DENIED); - - // Set schema1 - String prefix = PrefixUtil.createPrefix("package", "database"); - mAppSearchImpl.setSchema( - "package", - "database", - Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), - mVisibilityStore, - /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"), - /*schemasVisibleToPackages=*/ ImmutableMap.of( - "schema1", - ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))), - /*forceOverride=*/ false, - /*schemaVersion=*/ 0); - - // "schema1" is platform hidden now and package visible to package1 - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - mGlobalQuerierUid, - /*callerHasSystemAccess=*/ true)) - .isFalse(); - - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isTrue(); - - // Add a new schema, and include the already-existing "schema1" - mAppSearchImpl.setSchema( - "package", - "database", - ImmutableList.of( - new AppSearchSchema.Builder("schema1").build(), - new AppSearchSchema.Builder("schema2").build()), - mVisibilityStore, - /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"), - /*schemasVisibleToPackages=*/ ImmutableMap.of( - "schema1", - ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))), - /*forceOverride=*/ false, - /*schemaVersion=*/ 0); - - // Check that "schema1" still has the same visibility settings - SystemUtil.runWithShellPermissionIdentity(() -> assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - mGlobalQuerierUid, - /*callerHasSystemAccess=*/ true)) - .isFalse(), - READ_GLOBAL_APP_SEARCH_DATA); - - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isTrue(); - - // "schema2" has default visibility settings - SystemUtil.runWithShellPermissionIdentity(() -> assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema2", - mGlobalQuerierUid, - /*callerHasSystemAccess=*/ true)) - .isTrue(), - READ_GLOBAL_APP_SEARCH_DATA); - - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema2", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isFalse(); - } - - @Test - public void testRemoveSchema_removedFromVisibilityStore() throws Exception { - // Values for a "foo" client - String packageNameFoo = "packageFoo"; - byte[] sha256CertFoo = new byte[] {10}; - int uidFoo = 1; - - // Make sure foo package will pass package manager checks. - PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); - when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt())) - .thenReturn(uidFoo); - when(mockPackageManager.hasSigningCertificate( - packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256)) - .thenReturn(true); - - // Make sure we have global query privileges and "foo" doesn't - when(mockPackageManager.checkPermission( - READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName())) - .thenReturn(PERMISSION_GRANTED); - when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo)) - .thenReturn(PERMISSION_DENIED); - - String prefix = PrefixUtil.createPrefix("package", "database"); - mAppSearchImpl.setSchema( - "package", - "database", - Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), - mVisibilityStore, - /*schemasNotDisplayedBySystem=*/ Collections.singletonList("schema1"), - /*schemasVisibleToPackages=*/ ImmutableMap.of( - "schema1", - ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))), - /*forceOverride=*/ false, - /*schemaVersion=*/ 0); - - // "schema1" is platform hidden now and package accessible - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - mGlobalQuerierUid, - /*callerHasSystemAccess=*/ true)) - .isFalse(); - - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isTrue(); - - // Remove "schema1" by force overriding - mAppSearchImpl.setSchema( - "package", - "database", - /*schemas=*/ Collections.emptyList(), - mVisibilityStore, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ true, - /*schemaVersion=*/ 0); - - // Check that "schema1" is no longer considered platform hidden or package accessible - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - mGlobalQuerierUid, - /*callerHasSystemAccess=*/ true)) - .isTrue(); - - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isFalse(); - - // Add "schema1" back, it gets default visibility settings which means it's not platform - // hidden and not package accessible - mAppSearchImpl.setSchema( - "package", - "database", - Collections.singletonList(new AppSearchSchema.Builder("schema1").build()), - mVisibilityStore, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*schemaVersion=*/ 0); - - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - mGlobalQuerierUid, - /*callerHasSystemAccess=*/ true)) - .isTrue(); - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "schema1", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isFalse(); - } - - @Test - public void testSetSchema_defaultPlatformVisible() throws Exception { - // Make sure we have global query privileges - PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); - when(mockPackageManager.checkPermission( - READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName())) - .thenReturn(PERMISSION_GRANTED); - - String prefix = PrefixUtil.createPrefix("package", "database"); - mAppSearchImpl.setSchema( - "package", - "database", - Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), - mVisibilityStore, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*schemaVersion=*/ 0); - - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "Schema", - mGlobalQuerierUid, - /*callerHasSystemAccess=*/ true)) - .isTrue(); - } - - @Test - public void testSetSchema_platformHidden() throws Exception { - // Make sure we have global query privileges - PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); - when(mockPackageManager.checkPermission( - READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName())) - .thenReturn(PERMISSION_GRANTED); - - String prefix = PrefixUtil.createPrefix("package", "database"); - mAppSearchImpl.setSchema( - "package", - "database", - Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), - mVisibilityStore, - /*schemasNotDisplayedBySystem=*/ Collections.singletonList("Schema"), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*schemaVersion=*/ 0); - - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - prefix + "Schema", - mGlobalQuerierUid, - /*callerHasSystemAccess=*/ true)) - .isFalse(); - } - - @Test - public void testSetSchema_defaultNotVisibleToPackages() throws Exception { - String packageName = "com.package"; - - // Make sure package doesn't global query privileges - PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); - when(mockPackageManager.checkPermission( - READ_GLOBAL_APP_SEARCH_DATA, packageName)).thenReturn(PERMISSION_DENIED); - - String prefix = PrefixUtil.createPrefix("package", "database"); - mAppSearchImpl.setSchema( - "package", - "database", - Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), - mVisibilityStore, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*schemaVersion=*/ 0); - assertThat(mVisibilityStore - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "Schema", - /*callerUid=*/ 42, - /*callerHasSystemAccess=*/ false)) - .isFalse(); - } - - @Test - public void testSetSchema_visibleToPackages() throws Exception { - // Values for a "foo" client - String packageNameFoo = "packageFoo"; - byte[] sha256CertFoo = new byte[] {10}; - int uidFoo = 1; - - // Make sure foo package will pass package manager checks. - PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); - when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt())) - .thenReturn(uidFoo); - when(mockPackageManager.hasSigningCertificate( - packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256)) - .thenReturn(true); - - // Make sure foo doesn't have global query privileges - when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo)) - .thenReturn(PERMISSION_DENIED); - - String prefix = PrefixUtil.createPrefix("package", "database"); - mAppSearchImpl.setSchema( - "package", - "database", - Collections.singletonList(new AppSearchSchema.Builder("Schema").build()), - mVisibilityStore, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ ImmutableMap.of( - "Schema", - ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo))), - /*forceOverride=*/ false, - /*schemaVersion=*/ 0); - assertThat(mVisibilityStore - .isSchemaSearchableByCaller( - "package", - "database", - prefix + "Schema", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isTrue(); - } - - @NonNull - private PackageManager getMockPackageManager(@NonNull UserHandle user) { - PackageManager pm = mMockPackageManagers.get(user); - if (pm == null) { - pm = Mockito.mock(PackageManager.class); - mMockPackageManagers.put(user, pm); - } - return pm; - } -} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/FrameworkOptimizeStrategyTest.java b/services/tests/servicestests/src/com/android/server/appsearch/FrameworkOptimizeStrategyTest.java deleted file mode 100644 index 8389c85477ea8..0000000000000 --- a/services/tests/servicestests/src/com/android/server/appsearch/FrameworkOptimizeStrategyTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 com.android.server.appsearch.icing.proto.GetOptimizeInfoResultProto; -import com.android.server.appsearch.icing.proto.StatusProto; - -import org.junit.Test; - -public class FrameworkOptimizeStrategyTest { - AppSearchConfig mAppSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR); - FrameworkOptimizeStrategy mFrameworkOptimizeStrategy = - new FrameworkOptimizeStrategy(mAppSearchConfig); - - @Test - public void testShouldOptimize_byteThreshold() { - GetOptimizeInfoResultProto optimizeInfo = - GetOptimizeInfoResultProto.newBuilder() - .setTimeSinceLastOptimizeMs(0) - .setEstimatedOptimizableBytes( - mAppSearchConfig.getCachedBytesOptimizeThreshold()) - .setOptimizableDocs(0) - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK).build()) - .build(); - assertThat(mFrameworkOptimizeStrategy.shouldOptimize(optimizeInfo)).isTrue(); - } - - @Test - public void testShouldNotOptimize_timeThreshold() { - GetOptimizeInfoResultProto optimizeInfo = - GetOptimizeInfoResultProto.newBuilder() - .setTimeSinceLastOptimizeMs( - mAppSearchConfig.getCachedTimeOptimizeThresholdMs()) - .setEstimatedOptimizableBytes(0) - .setOptimizableDocs(0) - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK).build()) - .build(); - assertThat(mFrameworkOptimizeStrategy.shouldOptimize(optimizeInfo)).isTrue(); - } - - @Test - public void testShouldOptimize_docCountThreshold() { - GetOptimizeInfoResultProto optimizeInfo = - GetOptimizeInfoResultProto.newBuilder() - .setTimeSinceLastOptimizeMs(0) - .setEstimatedOptimizableBytes(0) - .setOptimizableDocs( - mAppSearchConfig.getCachedDocCountOptimizeThreshold()) - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.OK).build()) - .build(); - assertThat(mFrameworkOptimizeStrategy.shouldOptimize(optimizeInfo)).isTrue(); - } -} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/OWNERS b/services/tests/servicestests/src/com/android/server/appsearch/OWNERS deleted file mode 100644 index ebe9e4ec087f8..0000000000000 --- a/services/tests/servicestests/src/com/android/server/appsearch/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /apex/appsearch/OWNERS 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 deleted file mode 100644 index f40a5ad7bcb6b..0000000000000 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java +++ /dev/null @@ -1,3272 +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 com.android.server.appsearch.external.localstorage; - -import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.addPrefixToDocument; -import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix; -import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.removePrefixesFromDocument; - -import static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assert.assertThrows; - -import android.app.appsearch.AppSearchResult; -import android.app.appsearch.AppSearchSchema; -import android.app.appsearch.GenericDocument; -import android.app.appsearch.SearchResult; -import android.app.appsearch.SearchResultPage; -import android.app.appsearch.SearchSpec; -import android.app.appsearch.SetSchemaResponse; -import android.app.appsearch.StorageInfo; -import android.app.appsearch.exceptions.AppSearchException; -import android.content.Context; -import android.os.Process; -import android.util.ArrayMap; -import android.util.ArraySet; - -import androidx.test.core.app.ApplicationProvider; - -import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter; -import com.android.server.appsearch.external.localstorage.stats.InitializeStats; -import com.android.server.appsearch.external.localstorage.stats.OptimizeStats; -import com.android.server.appsearch.external.localstorage.util.PrefixUtil; -import com.android.server.appsearch.icing.proto.DocumentProto; -import com.android.server.appsearch.icing.proto.GetOptimizeInfoResultProto; -import com.android.server.appsearch.icing.proto.PersistType; -import com.android.server.appsearch.icing.proto.PropertyConfigProto; -import com.android.server.appsearch.icing.proto.PropertyProto; -import com.android.server.appsearch.icing.proto.PutResultProto; -import com.android.server.appsearch.icing.proto.SchemaProto; -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; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class AppSearchImplTest { - @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); - private AppSearchImpl mAppSearchImpl; - /** - * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class. - */ - private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true; - - @Before - public void setUp() throws Exception { - mAppSearchImpl = - AppSearchImpl.create( - mTemporaryFolder.newFolder(), - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - } - - /** - * 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 - * schema. - */ - @Test - public void testRewriteSchema_addType() throws Exception { - SchemaProto.Builder existingSchemaBuilder = - SchemaProto.newBuilder() - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$existingDatabase/Foo") - .build()); - - // Create a copy so we can modify it. - List existingTypes = - new ArrayList<>(existingSchemaBuilder.getTypesList()); - SchemaTypeConfigProto schemaTypeConfigProto1 = - SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build(); - SchemaTypeConfigProto schemaTypeConfigProto2 = - SchemaTypeConfigProto.newBuilder() - .setSchemaType("TestType") - .addProperties( - PropertyConfigProto.newBuilder() - .setPropertyName("subject") - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setCardinality( - PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setStringIndexingConfig( - StringIndexingConfig.newBuilder() - .setTokenizerType( - StringIndexingConfig.TokenizerType - .Code.PLAIN) - .setTermMatchType(TermMatchType.Code.PREFIX) - .build()) - .build()) - .addProperties( - PropertyConfigProto.newBuilder() - .setPropertyName("link") - .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT) - .setCardinality( - PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setSchemaType("RefType") - .build()) - .build(); - SchemaTypeConfigProto schemaTypeConfigProto3 = - SchemaTypeConfigProto.newBuilder().setSchemaType("RefType").build(); - SchemaProto newSchema = - SchemaProto.newBuilder() - .addTypes(schemaTypeConfigProto1) - .addTypes(schemaTypeConfigProto2) - .addTypes(schemaTypeConfigProto3) - .build(); - - AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = - AppSearchImpl.rewriteSchema( - createPrefix("package", "newDatabase"), existingSchemaBuilder, newSchema); - - // We rewrote all the new types that were added. And nothing was removed. - assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet()) - .containsExactly( - "package$newDatabase/Foo", - "package$newDatabase/TestType", - "package$newDatabase/RefType"); - assertThat( - rewrittenSchemaResults - .mRewrittenPrefixedTypes - .get("package$newDatabase/Foo") - .getSchemaType()) - .isEqualTo("package$newDatabase/Foo"); - assertThat( - rewrittenSchemaResults - .mRewrittenPrefixedTypes - .get("package$newDatabase/TestType") - .getSchemaType()) - .isEqualTo("package$newDatabase/TestType"); - assertThat( - rewrittenSchemaResults - .mRewrittenPrefixedTypes - .get("package$newDatabase/RefType") - .getSchemaType()) - .isEqualTo("package$newDatabase/RefType"); - assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty(); - - SchemaProto expectedSchema = - SchemaProto.newBuilder() - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$newDatabase/Foo") - .build()) - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$newDatabase/TestType") - .addProperties( - PropertyConfigProto.newBuilder() - .setPropertyName("subject") - .setDataType( - PropertyConfigProto.DataType.Code - .STRING) - .setCardinality( - PropertyConfigProto.Cardinality.Code - .OPTIONAL) - .setStringIndexingConfig( - StringIndexingConfig.newBuilder() - .setTokenizerType( - StringIndexingConfig - .TokenizerType - .Code.PLAIN) - .setTermMatchType( - TermMatchType.Code - .PREFIX) - .build()) - .build()) - .addProperties( - PropertyConfigProto.newBuilder() - .setPropertyName("link") - .setDataType( - PropertyConfigProto.DataType.Code - .DOCUMENT) - .setCardinality( - PropertyConfigProto.Cardinality.Code - .OPTIONAL) - .setSchemaType( - "package$newDatabase/RefType") - .build()) - .build()) - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$newDatabase/RefType") - .build()) - .build(); - - existingTypes.addAll(expectedSchema.getTypesList()); - assertThat(existingSchemaBuilder.getTypesList()).containsExactlyElementsIn(existingTypes); - } - - /** - * Ensure that we track all types that were rewritten in the input schema. Even if they were not - * technically "added" to the existing schema. - */ - @Test - public void testRewriteSchema_rewriteType() throws Exception { - SchemaProto.Builder existingSchemaBuilder = - SchemaProto.newBuilder() - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$existingDatabase/Foo") - .build()); - - SchemaProto newSchema = - SchemaProto.newBuilder() - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Foo").build()) - .build(); - - AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = - AppSearchImpl.rewriteSchema( - createPrefix("package", "existingDatabase"), - existingSchemaBuilder, - newSchema); - - // Nothing was removed, but the method did rewrite the type name. - assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet()) - .containsExactly("package$existingDatabase/Foo"); - assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty(); - - // Same schema since nothing was added. - SchemaProto expectedSchema = existingSchemaBuilder.build(); - assertThat(existingSchemaBuilder.getTypesList()) - .containsExactlyElementsIn(expectedSchema.getTypesList()); - } - - /** - * Ensure that we track which types from the existing schema are deleted when a new schema is - * set. - */ - @Test - public void testRewriteSchema_deleteType() throws Exception { - SchemaProto.Builder existingSchemaBuilder = - SchemaProto.newBuilder() - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$existingDatabase/Foo") - .build()); - - SchemaProto newSchema = - SchemaProto.newBuilder() - .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("Bar").build()) - .build(); - - AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = - AppSearchImpl.rewriteSchema( - createPrefix("package", "existingDatabase"), - existingSchemaBuilder, - newSchema); - - // Bar type was rewritten, but Foo ended up being deleted since it wasn't included in the - // new schema. - assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes) - .containsKey("package$existingDatabase/Bar"); - assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes.keySet().size()).isEqualTo(1); - assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes) - .containsExactly("package$existingDatabase/Foo"); - - // Same schema since nothing was added. - SchemaProto expectedSchema = - SchemaProto.newBuilder() - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$existingDatabase/Bar") - .build()) - .build(); - - assertThat(existingSchemaBuilder.getTypesList()) - .containsExactlyElementsIn(expectedSchema.getTypesList()); - } - - @Test - public void testAddDocumentTypePrefix() { - DocumentProto insideDocument = - DocumentProto.newBuilder() - .setUri("inside-id") - .setSchema("type") - .setNamespace("namespace") - .build(); - DocumentProto documentProto = - DocumentProto.newBuilder() - .setUri("id") - .setSchema("type") - .setNamespace("namespace") - .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) - .build(); - - DocumentProto expectedInsideDocument = - DocumentProto.newBuilder() - .setUri("inside-id") - .setSchema("package$databaseName/type") - .setNamespace("package$databaseName/namespace") - .build(); - DocumentProto expectedDocumentProto = - DocumentProto.newBuilder() - .setUri("id") - .setSchema("package$databaseName/type") - .setNamespace("package$databaseName/namespace") - .addProperties( - PropertyProto.newBuilder() - .addDocumentValues(expectedInsideDocument)) - .build(); - - DocumentProto.Builder actualDocument = documentProto.toBuilder(); - addPrefixToDocument(actualDocument, createPrefix("package", "databaseName")); - assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto); - } - - @Test - public void testRemoveDocumentTypePrefixes() throws Exception { - DocumentProto insideDocument = - DocumentProto.newBuilder() - .setUri("inside-id") - .setSchema("package$databaseName/type") - .setNamespace("package$databaseName/namespace") - .build(); - DocumentProto documentProto = - DocumentProto.newBuilder() - .setUri("id") - .setSchema("package$databaseName/type") - .setNamespace("package$databaseName/namespace") - .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) - .build(); - - DocumentProto expectedInsideDocument = - DocumentProto.newBuilder() - .setUri("inside-id") - .setSchema("type") - .setNamespace("namespace") - .build(); - - DocumentProto expectedDocumentProto = - DocumentProto.newBuilder() - .setUri("id") - .setSchema("type") - .setNamespace("namespace") - .addProperties( - PropertyProto.newBuilder() - .addDocumentValues(expectedInsideDocument)) - .build(); - - DocumentProto.Builder actualDocument = documentProto.toBuilder(); - assertThat(removePrefixesFromDocument(actualDocument)).isEqualTo("package$databaseName/"); - assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto); - } - - @Test - public void testRemoveDatabasesFromDocumentThrowsException() { - // Set two different database names in the document, which should never happen - DocumentProto documentProto = - DocumentProto.newBuilder() - .setUri("id") - .setSchema("prefix1/type") - .setNamespace("prefix2/namespace") - .build(); - - DocumentProto.Builder actualDocument = documentProto.toBuilder(); - AppSearchException e = - assertThrows( - AppSearchException.class, () -> removePrefixesFromDocument(actualDocument)); - assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names"); - } - - @Test - public void testNestedRemoveDatabasesFromDocumentThrowsException() { - // Set two different database names in the outer and inner document, which should never - // happen. - DocumentProto insideDocument = - DocumentProto.newBuilder() - .setUri("inside-id") - .setSchema("prefix1/type") - .setNamespace("prefix1/namespace") - .build(); - DocumentProto documentProto = - DocumentProto.newBuilder() - .setUri("id") - .setSchema("prefix2/type") - .setNamespace("prefix2/namespace") - .addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument)) - .build(); - - DocumentProto.Builder actualDocument = documentProto.toBuilder(); - AppSearchException e = - assertThrows( - AppSearchException.class, () -> removePrefixesFromDocument(actualDocument)); - assertThat(e).hasMessageThat().contains("Found unexpected multiple prefix names"); - } - - @Test - public void testTriggerCheckOptimizeByMutationSize() throws Exception { - // Insert schema - List 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 and then remove it to generate garbage. - GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build(); - mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null); - mAppSearchImpl.remove( - "package", "database", "namespace", "id", /*removeStatsBuilder=*/ null); - - // Verify there is garbage documents. - GetOptimizeInfoResultProto optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked(); - assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(1); - - // Increase mutation counter and stop before reach the threshold - mAppSearchImpl.checkForOptimize( - AppSearchImpl.CHECK_OPTIMIZE_INTERVAL - 1, /*builder=*/ null); - - // Verify the optimize() isn't triggered. - optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked(); - assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(1); - - // Increase the counter and reach the threshold, optimize() should be triggered. - OptimizeStats.Builder builder = new OptimizeStats.Builder(); - mAppSearchImpl.checkForOptimize(/*mutateBatchSize=*/ 1, builder); - - // Verify optimize() is triggered. - optimizeInfo = mAppSearchImpl.getOptimizeInfoResultLocked(); - assertThat(optimizeInfo.getOptimizableDocs()).isEqualTo(0); - assertThat(optimizeInfo.getEstimatedOptimizableBytes()).isEqualTo(0); - - // Verify the stats have been set. - OptimizeStats oStats = builder.build(); - assertThat(oStats.getOriginalDocumentCount()).isEqualTo(1); - assertThat(oStats.getDeletedDocumentCount()).isEqualTo(1); - } - - @Test - public void testReset() throws Exception { - // Setup the index - Context context = ApplicationProvider.getApplicationContext(); - File appsearchDir = mTemporaryFolder.newFolder(); - AppSearchImpl appSearchImpl = - AppSearchImpl.create( - appsearchDir, - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - - // Insert schema - List schemas = - ImmutableList.of( - new AppSearchSchema.Builder("Type1").build(), - new AppSearchSchema.Builder("Type2").build()); - appSearchImpl.setSchema( - context.getPackageName(), - "database1", - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ 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=*/ null, - Process.INVALID_UID, - /*callerHasSystemAccess=*/ false, - /*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 = - assertThrows( - 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); - - // Initialize AppSearchImpl. This should cause a reset. - InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder(); - appSearchImpl.close(); - appSearchImpl = - AppSearchImpl.create( - appsearchDir, - new UnlimitedLimitConfig(), - initStatsBuilder, - ALWAYS_OPTIMIZE); - - // Check recovery state - InitializeStats initStats = initStatsBuilder.build(); - assertThat(initStats).isNotNull(); - assertThat(initStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR); - assertThat(initStats.hasDeSync()).isFalse(); - assertThat(initStats.getDocumentStoreRecoveryCause()) - .isEqualTo(InitializeStats.RECOVERY_CAUSE_NONE); - 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=*/ null, - Process.INVALID_UID, - /*callerHasSystemAccess=*/ false, - /*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()), - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ 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=*/ null, - Process.INVALID_UID, - /*callerHasSystemAccess=*/ false, - /*logger=*/ null); - assertThat(results.getResults()).hasSize(1); - assertThat(results.getResults().get(0).getGenericDocument()).isEqualTo(validDoc); - } - - @Test - public void testRewriteSearchSpec_oneInstance() throws Exception { - SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery(""); - - // Insert schema - List 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 document - GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build(); - mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null); - - // Rewrite SearchSpec - mAppSearchImpl.rewriteSearchSpecForPrefixesLocked( - searchSpecProto, - Collections.singleton(createPrefix("package", "database")), - ImmutableSet.of("package$database/type")); - assertThat(searchSpecProto.getSchemaTypeFiltersList()) - .containsExactly("package$database/type"); - assertThat(searchSpecProto.getNamespaceFiltersList()) - .containsExactly("package$database/namespace"); - } - - @Test - public void testRewriteSearchSpec_twoInstances() throws Exception { - SearchSpecProto.Builder searchSpecProto = SearchSpecProto.newBuilder().setQuery(""); - - // Insert schema - List schemas = - ImmutableList.of( - new AppSearchSchema.Builder("typeA").build(), - new AppSearchSchema.Builder("typeB").build()); - mAppSearchImpl.setSchema( - "package", - "database1", - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - mAppSearchImpl.setSchema( - "package", - "database2", - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert documents - GenericDocument document1 = - new GenericDocument.Builder<>("namespace", "id", "typeA").build(); - mAppSearchImpl.putDocument("package", "database1", document1, /*logger=*/ null); - - GenericDocument document2 = - new GenericDocument.Builder<>("namespace", "id", "typeB").build(); - mAppSearchImpl.putDocument("package", "database2", document2, /*logger=*/ null); - - // Rewrite SearchSpec - mAppSearchImpl.rewriteSearchSpecForPrefixesLocked( - searchSpecProto, - ImmutableSet.of( - createPrefix("package", "database1"), createPrefix("package", "database2")), - ImmutableSet.of( - "package$database1/typeA", "package$database1/typeB", - "package$database2/typeA", "package$database2/typeB")); - assertThat(searchSpecProto.getSchemaTypeFiltersList()) - .containsExactly( - "package$database1/typeA", - "package$database1/typeB", - "package$database2/typeA", - "package$database2/typeB"); - assertThat(searchSpecProto.getNamespaceFiltersList()) - .containsExactly("package$database1/namespace", "package$database2/namespace"); - } - - @Test - public void testRewriteSearchSpec_ignoresSearchSpecSchemaFilters() throws Exception { - SearchSpecProto.Builder searchSpecProto = - SearchSpecProto.newBuilder().setQuery("").addSchemaTypeFilters("type"); - - // Insert schema - List 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 document - GenericDocument document = new GenericDocument.Builder<>("namespace", "id", "type").build(); - mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null); - - // If 'allowedPrefixedSchemas' is empty, this returns false since there's nothing to - // search over. Despite the searchSpecProto having schema type filters. - assertThat( - mAppSearchImpl.rewriteSearchSpecForPrefixesLocked( - searchSpecProto, - Collections.singleton(createPrefix("package", "database")), - /*allowedPrefixedSchemas=*/ Collections.emptySet())) - .isFalse(); - } - - @Test - public void testQueryEmptyDatabase() throws Exception { - SearchSpec searchSpec = - new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build(); - SearchResultPage searchResultPage = - mAppSearchImpl.query("package", "EmptyDatabase", "", searchSpec, /*logger=*/ null); - assertThat(searchResultPage.getResults()).isEmpty(); - } - - /** - * TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term - * test until we have official support for multiple-apps indexing at once. - */ - @Test - public void testQueryWithMultiplePackages_noPackageFilters() throws Exception { - // Insert package1 schema - List schema1 = - ImmutableList.of(new AppSearchSchema.Builder("schema1").build()); - mAppSearchImpl.setSchema( - "package1", - "database1", - schema1, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert package2 schema - List schema2 = - ImmutableList.of(new AppSearchSchema.Builder("schema2").build()); - mAppSearchImpl.setSchema( - "package2", - "database2", - schema2, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert package1 document - GenericDocument document = - new GenericDocument.Builder<>("namespace", "id", "schema1").build(); - mAppSearchImpl.putDocument("package1", "database1", document, /*logger=*/ null); - - // No query filters specified, package2 shouldn't be able to query for package1's documents. - SearchSpec searchSpec = - new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build(); - SearchResultPage searchResultPage = - mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=*/ null); - assertThat(searchResultPage.getResults()).isEmpty(); - - // Insert package2 document - document = new GenericDocument.Builder<>("namespace", "id", "schema2").build(); - mAppSearchImpl.putDocument("package2", "database2", document, /*logger=*/ null); - - // No query filters specified. package2 should only get its own documents back. - searchResultPage = - mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger= - */ null); - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document); - } - - /** - * TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term - * test until we have official support for multiple-apps indexing at once. - */ - @Test - public void testQueryWithMultiplePackages_withPackageFilters() throws Exception { - // Insert package1 schema - List schema1 = - ImmutableList.of(new AppSearchSchema.Builder("schema1").build()); - mAppSearchImpl.setSchema( - "package1", - "database1", - schema1, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert package2 schema - List schema2 = - ImmutableList.of(new AppSearchSchema.Builder("schema2").build()); - mAppSearchImpl.setSchema( - "package2", - "database2", - schema2, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert package1 document - GenericDocument document = - new GenericDocument.Builder<>("namespace", "id", "schema1").build(); - mAppSearchImpl.putDocument("package1", "database1", document, /*logger=*/ null); - - // "package1" filter specified, but package2 shouldn't be able to query for package1's - // documents. - SearchSpec searchSpec = - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .addFilterPackageNames("package1") - .build(); - SearchResultPage searchResultPage = - mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger=*/ null); - assertThat(searchResultPage.getResults()).isEmpty(); - - // Insert package2 document - document = new GenericDocument.Builder<>("namespace", "id", "schema2").build(); - mAppSearchImpl.putDocument("package2", "database2", document, /*logger=*/ null); - - // "package2" filter specified, package2 should only get its own documents back. - searchSpec = - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .addFilterPackageNames("package2") - .build(); - searchResultPage = - mAppSearchImpl.query("package2", "database2", "", searchSpec, /*logger= - */ null); - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document); - } - - @Test - public void testGlobalQueryEmptyDatabase() throws Exception { - SearchSpec searchSpec = - new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build(); - SearchResultPage searchResultPage = - mAppSearchImpl.globalQuery( - "", - searchSpec, - /*callerPackageName=*/ "", - /*visibilityStore=*/ null, - Process.INVALID_UID, - /*callerHasSystemAccess=*/ false, - /*logger=*/ null); - assertThat(searchResultPage.getResults()).isEmpty(); - } - - @Test - public void testGetNextPageToken_query() throws Exception { - // Insert package1 schema - List schema1 = - ImmutableList.of(new AppSearchSchema.Builder("schema1").build()); - mAppSearchImpl.setSchema( - "package1", - "database1", - schema1, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert two package1 documents - GenericDocument document1 = - new GenericDocument.Builder<>("namespace", "id1", "schema1").build(); - GenericDocument document2 = - new GenericDocument.Builder<>("namespace", "id2", "schema1").build(); - mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null); - mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null); - - // Query for only 1 result per page - SearchSpec searchSpec = - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .setResultCountPerPage(1) - .build(); - SearchResultPage searchResultPage = - mAppSearchImpl.query("package1", "database1", "", searchSpec, /*logger=*/ null); - - // Document2 will come first because it was inserted last and default return order is - // most recent. - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2); - - long nextPageToken = searchResultPage.getNextPageToken(); - searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken); - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1); - } - - @Test - public void testGetNextPageWithDifferentPackage_query() throws Exception { - // Insert package1 schema - List schema1 = - ImmutableList.of(new AppSearchSchema.Builder("schema1").build()); - mAppSearchImpl.setSchema( - "package1", - "database1", - schema1, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert two package1 documents - GenericDocument document1 = - new GenericDocument.Builder<>("namespace", "id1", "schema1").build(); - GenericDocument document2 = - new GenericDocument.Builder<>("namespace", "id2", "schema1").build(); - mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null); - mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null); - - // Query for only 1 result per page - SearchSpec searchSpec = - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .setResultCountPerPage(1) - .build(); - SearchResultPage searchResultPage = - mAppSearchImpl.query("package1", "database1", "", searchSpec, /*logger=*/ null); - - // Document2 will come first because it was inserted last and default return order is - // most recent. - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2); - - long nextPageToken = searchResultPage.getNextPageToken(); - - // Try getting next page with the wrong package, package2 - AppSearchException e = - assertThrows( - AppSearchException.class, - () -> mAppSearchImpl.getNextPage("package2", nextPageToken)); - assertThat(e) - .hasMessageThat() - .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken); - assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR); - - // Can continue getting next page for package1 - searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken); - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1); - } - - @Test - public void testGetNextPageToken_globalQuery() throws Exception { - // Insert package1 schema - List schema1 = - ImmutableList.of(new AppSearchSchema.Builder("schema1").build()); - mAppSearchImpl.setSchema( - "package1", - "database1", - schema1, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert two package1 documents - GenericDocument document1 = - new GenericDocument.Builder<>("namespace", "id1", "schema1").build(); - GenericDocument document2 = - new GenericDocument.Builder<>("namespace", "id2", "schema1").build(); - mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null); - mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null); - - // Query for only 1 result per page - SearchSpec searchSpec = - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .setResultCountPerPage(1) - .build(); - SearchResultPage searchResultPage = - mAppSearchImpl.globalQuery( - /*queryExpression=*/ "", - searchSpec, - "package1", - /*visibilityStore=*/ null, - Process.myUid(), - /*callerHasSystemAccess=*/ false, - /*logger=*/ null); - - // Document2 will come first because it was inserted last and default return order is - // most recent. - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2); - - long nextPageToken = searchResultPage.getNextPageToken(); - searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken); - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1); - } - - @Test - public void testGetNextPageWithDifferentPackage_globalQuery() throws Exception { - // Insert package1 schema - List schema1 = - ImmutableList.of(new AppSearchSchema.Builder("schema1").build()); - mAppSearchImpl.setSchema( - "package1", - "database1", - schema1, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert two package1 documents - GenericDocument document1 = - new GenericDocument.Builder<>("namespace", "id1", "schema1").build(); - GenericDocument document2 = - new GenericDocument.Builder<>("namespace", "id2", "schema1").build(); - mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null); - mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null); - - // Query for only 1 result per page - SearchSpec searchSpec = - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .setResultCountPerPage(1) - .build(); - SearchResultPage searchResultPage = - mAppSearchImpl.globalQuery( - /*queryExpression=*/ "", - searchSpec, - "package1", - /*visibilityStore=*/ null, - Process.myUid(), - /*callerHasSystemAccess=*/ false, - /*logger=*/ null); - - // Document2 will come first because it was inserted last and default return order is - // most recent. - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2); - - long nextPageToken = searchResultPage.getNextPageToken(); - - // Try getting next page with the wrong package, package2 - AppSearchException e = - assertThrows( - AppSearchException.class, - () -> mAppSearchImpl.getNextPage("package2", nextPageToken)); - assertThat(e) - .hasMessageThat() - .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken); - assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR); - - // Can continue getting next page for package1 - searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken); - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1); - } - - @Test - public void testInvalidateNextPageToken_query() throws Exception { - // Insert package1 schema - List schema1 = - ImmutableList.of(new AppSearchSchema.Builder("schema1").build()); - mAppSearchImpl.setSchema( - "package1", - "database1", - schema1, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert two package1 documents - GenericDocument document1 = - new GenericDocument.Builder<>("namespace", "id1", "schema1").build(); - GenericDocument document2 = - new GenericDocument.Builder<>("namespace", "id2", "schema1").build(); - mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null); - mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null); - - // Query for only 1 result per page - SearchSpec searchSpec = - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .setResultCountPerPage(1) - .build(); - SearchResultPage searchResultPage = - mAppSearchImpl.query("package1", "database1", "", searchSpec, /*logger=*/ null); - - // Document2 will come first because it was inserted last and default return order is - // most recent. - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2); - - long nextPageToken = searchResultPage.getNextPageToken(); - - // Invalidate the token - mAppSearchImpl.invalidateNextPageToken("package1", nextPageToken); - - // Can't get next page because we invalidated the token. - AppSearchException e = - assertThrows( - AppSearchException.class, - () -> mAppSearchImpl.getNextPage("package1", nextPageToken)); - assertThat(e) - .hasMessageThat() - .contains("Package \"package1\" cannot use nextPageToken: " + nextPageToken); - assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR); - } - - @Test - public void testInvalidateNextPageTokenWithDifferentPackage_query() throws Exception { - // Insert package1 schema - List schema1 = - ImmutableList.of(new AppSearchSchema.Builder("schema1").build()); - mAppSearchImpl.setSchema( - "package1", - "database1", - schema1, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert two package1 documents - GenericDocument document1 = - new GenericDocument.Builder<>("namespace", "id1", "schema1").build(); - GenericDocument document2 = - new GenericDocument.Builder<>("namespace", "id2", "schema1").build(); - mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null); - mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null); - - // Query for only 1 result per page - SearchSpec searchSpec = - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .setResultCountPerPage(1) - .build(); - SearchResultPage searchResultPage = - mAppSearchImpl.query("package1", "database1", "", searchSpec, /*logger=*/ null); - - // Document2 will come first because it was inserted last and default return order is - // most recent. - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2); - - long nextPageToken = searchResultPage.getNextPageToken(); - - // Try getting next page with the wrong package, package2 - AppSearchException e = - assertThrows( - AppSearchException.class, - () -> mAppSearchImpl.invalidateNextPageToken("package2", nextPageToken)); - assertThat(e) - .hasMessageThat() - .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken); - assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR); - - // Can continue getting next page for package1 - searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken); - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1); - } - - @Test - public void testInvalidateNextPageToken_globalQuery() throws Exception { - // Insert package1 schema - List schema1 = - ImmutableList.of(new AppSearchSchema.Builder("schema1").build()); - mAppSearchImpl.setSchema( - "package1", - "database1", - schema1, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert two package1 documents - GenericDocument document1 = - new GenericDocument.Builder<>("namespace", "id1", "schema1").build(); - GenericDocument document2 = - new GenericDocument.Builder<>("namespace", "id2", "schema1").build(); - mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null); - mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null); - - // Query for only 1 result per page - SearchSpec searchSpec = - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .setResultCountPerPage(1) - .build(); - SearchResultPage searchResultPage = - mAppSearchImpl.globalQuery( - /*queryExpression=*/ "", - searchSpec, - "package1", - /*visibilityStore=*/ null, - Process.myUid(), - /*callerHasSystemAccess=*/ false, - /*logger=*/ null); - - // Document2 will come first because it was inserted last and default return order is - // most recent. - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2); - - long nextPageToken = searchResultPage.getNextPageToken(); - - // Invalidate the token - mAppSearchImpl.invalidateNextPageToken("package1", nextPageToken); - - // Can't get next page because we invalidated the token. - AppSearchException e = - assertThrows( - AppSearchException.class, - () -> mAppSearchImpl.getNextPage("package1", nextPageToken)); - assertThat(e) - .hasMessageThat() - .contains("Package \"package1\" cannot use nextPageToken: " + nextPageToken); - assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR); - } - - @Test - public void testInvalidateNextPageTokenWithDifferentPackage_globalQuery() throws Exception { - // Insert package1 schema - List schema1 = - ImmutableList.of(new AppSearchSchema.Builder("schema1").build()); - mAppSearchImpl.setSchema( - "package1", - "database1", - schema1, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert two package1 documents - GenericDocument document1 = - new GenericDocument.Builder<>("namespace", "id1", "schema1").build(); - GenericDocument document2 = - new GenericDocument.Builder<>("namespace", "id2", "schema1").build(); - mAppSearchImpl.putDocument("package1", "database1", document1, /*logger=*/ null); - mAppSearchImpl.putDocument("package1", "database1", document2, /*logger=*/ null); - - // Query for only 1 result per page - SearchSpec searchSpec = - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .setResultCountPerPage(1) - .build(); - SearchResultPage searchResultPage = - mAppSearchImpl.globalQuery( - /*queryExpression=*/ "", - searchSpec, - "package1", - /*visibilityStore=*/ null, - Process.myUid(), - /*callerHasSystemAccess=*/ false, - /*logger=*/ null); - - // Document2 will come first because it was inserted last and default return order is - // most recent. - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2); - - long nextPageToken = searchResultPage.getNextPageToken(); - - // Try getting next page with the wrong package, package2 - AppSearchException e = - assertThrows( - AppSearchException.class, - () -> mAppSearchImpl.invalidateNextPageToken("package2", nextPageToken)); - assertThat(e) - .hasMessageThat() - .contains("Package \"package2\" cannot use nextPageToken: " + nextPageToken); - assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR); - - // Can continue getting next page for package1 - searchResultPage = mAppSearchImpl.getNextPage("package1", nextPageToken); - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document1); - } - - @Test - public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception { - SearchSpec searchSpec = - new SearchSpec.Builder() - .addFilterSchemas("FakeType") - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .build(); - mAppSearchImpl.removeByQuery( - "package", "EmptyDatabase", "", searchSpec, /*statsBuilder=*/ null); - - searchSpec = - new SearchSpec.Builder() - .addFilterNamespaces("FakeNamespace") - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .build(); - mAppSearchImpl.removeByQuery( - "package", "EmptyDatabase", "", searchSpec, /*statsBuilder=*/ null); - - searchSpec = new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build(); - mAppSearchImpl.removeByQuery( - "package", "EmptyDatabase", "", searchSpec, /*statsBuilder=*/ null); - } - - @Test - public void testSetSchema() throws Exception { - List existingSchemas = - mAppSearchImpl.getSchemaProtoLocked().getTypesList(); - - List schemas = - Collections.singletonList(new AppSearchSchema.Builder("Email").build()); - // Set schema Email to AppSearch database1 - mAppSearchImpl.setSchema( - "package", - "database1", - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Create expected schemaType proto. - SchemaProto expectedProto = - SchemaProto.newBuilder() - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$database1/Email") - .setVersion(0)) - .build(); - - List expectedTypes = new ArrayList<>(); - expectedTypes.addAll(existingSchemas); - expectedTypes.addAll(expectedProto.getTypesList()); - assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) - .containsExactlyElementsIn(expectedTypes); - } - - @Test - public void testSetSchema_incompatible() throws Exception { - List oldSchemas = new ArrayList<>(); - oldSchemas.add( - new AppSearchSchema.Builder("Email") - .addProperty( - new AppSearchSchema.StringPropertyConfig.Builder("foo") - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED) - .setTokenizerType( - AppSearchSchema.StringPropertyConfig - .TOKENIZER_TYPE_PLAIN) - .setIndexingType( - AppSearchSchema.StringPropertyConfig - .INDEXING_TYPE_PREFIXES) - .build()) - .build()); - oldSchemas.add(new AppSearchSchema.Builder("Text").build()); - // Set schema Email to AppSearch database1 - mAppSearchImpl.setSchema( - "package", - "database1", - oldSchemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Create incompatible schema - List newSchemas = - Collections.singletonList(new AppSearchSchema.Builder("Email").build()); - - // set email incompatible and delete text - SetSchemaResponse setSchemaResponse = - mAppSearchImpl.setSchema( - "package", - "database1", - newSchemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ true, - /*version=*/ 0); - assertThat(setSchemaResponse.getDeletedTypes()).containsExactly("Text"); - assertThat(setSchemaResponse.getIncompatibleTypes()).containsExactly("Email"); - } - - @Test - public void testRemoveSchema() throws Exception { - List existingSchemas = - mAppSearchImpl.getSchemaProtoLocked().getTypesList(); - - List schemas = - ImmutableList.of( - new AppSearchSchema.Builder("Email").build(), - new AppSearchSchema.Builder("Document").build()); - // Set schema Email and Document to AppSearch database1 - mAppSearchImpl.setSchema( - "package", - "database1", - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Create expected schemaType proto. - SchemaProto expectedProto = - SchemaProto.newBuilder() - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$database1/Email") - .setVersion(0)) - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$database1/Document") - .setVersion(0)) - .build(); - - // Check both schema Email and Document saved correctly. - List expectedTypes = new ArrayList<>(); - expectedTypes.addAll(existingSchemas); - expectedTypes.addAll(expectedProto.getTypesList()); - assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) - .containsExactlyElementsIn(expectedTypes); - - final List finalSchemas = - Collections.singletonList(new AppSearchSchema.Builder("Email").build()); - SetSchemaResponse setSchemaResponse = - mAppSearchImpl.setSchema( - "package", - "database1", - finalSchemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Check the Document type has been deleted. - assertThat(setSchemaResponse.getDeletedTypes()).containsExactly("Document"); - - // ForceOverride to delete. - mAppSearchImpl.setSchema( - "package", - "database1", - finalSchemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ true, - /*version=*/ 0); - - // Check Document schema is removed. - expectedProto = - SchemaProto.newBuilder() - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$database1/Email") - .setVersion(0)) - .build(); - - expectedTypes = new ArrayList<>(); - expectedTypes.addAll(existingSchemas); - expectedTypes.addAll(expectedProto.getTypesList()); - assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) - .containsExactlyElementsIn(expectedTypes); - } - - @Test - public void testRemoveSchema_differentDataBase() throws Exception { - List existingSchemas = - mAppSearchImpl.getSchemaProtoLocked().getTypesList(); - - // Create schemas - List schemas = - ImmutableList.of( - new AppSearchSchema.Builder("Email").build(), - new AppSearchSchema.Builder("Document").build()); - - // Set schema Email and Document to AppSearch database1 and 2 - mAppSearchImpl.setSchema( - "package", - "database1", - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - mAppSearchImpl.setSchema( - "package", - "database2", - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Create expected schemaType proto. - SchemaProto expectedProto = - SchemaProto.newBuilder() - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$database1/Email") - .setVersion(0)) - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$database1/Document") - .setVersion(0)) - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$database2/Email") - .setVersion(0)) - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$database2/Document") - .setVersion(0)) - .build(); - - // Check Email and Document is saved in database 1 and 2 correctly. - List expectedTypes = new ArrayList<>(); - expectedTypes.addAll(existingSchemas); - expectedTypes.addAll(expectedProto.getTypesList()); - assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) - .containsExactlyElementsIn(expectedTypes); - - // Save only Email to database1 this time. - schemas = Collections.singletonList(new AppSearchSchema.Builder("Email").build()); - mAppSearchImpl.setSchema( - "package", - "database1", - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ true, - /*version=*/ 0); - - // Create expected schemaType list, database 1 should only contain Email but database 2 - // remains in same. - expectedProto = - SchemaProto.newBuilder() - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$database1/Email") - .setVersion(0)) - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$database2/Email") - .setVersion(0)) - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("package$database2/Document") - .setVersion(0)) - .build(); - - // Check nothing changed in database2. - expectedTypes = new ArrayList<>(); - expectedTypes.addAll(existingSchemas); - expectedTypes.addAll(expectedProto.getTypesList()); - assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) - .containsExactlyElementsIn(expectedTypes); - } - - @Test - public void testClearPackageData() throws AppSearchException { - List existingSchemas = - mAppSearchImpl.getSchemaProtoLocked().getTypesList(); - Map> existingDatabases = mAppSearchImpl.getPackageToDatabases(); - - // Insert package schema - List schema = - ImmutableList.of(new AppSearchSchema.Builder("schema").build()); - mAppSearchImpl.setSchema( - "package", - "database", - schema, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert package document - GenericDocument document = - new GenericDocument.Builder<>("namespace", "id", "schema").build(); - mAppSearchImpl.putDocument("package", "database", document, /*logger=*/ null); - - // Verify the document is indexed. - SearchSpec searchSpec = - new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build(); - SearchResultPage searchResultPage = - mAppSearchImpl.query( - "package", - "database", - /*queryExpression=*/ "", - searchSpec, - /*logger=*/ null); - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document); - - // Remove the package - mAppSearchImpl.clearPackageData("package"); - - // Verify the document is cleared. - searchResultPage = - mAppSearchImpl.query( - "package2", - "database2", - /*queryExpression=*/ "", - searchSpec, - /*logger=*/ null); - assertThat(searchResultPage.getResults()).isEmpty(); - - // Verify the schema is cleared. - assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) - .containsExactlyElementsIn(existingSchemas); - assertThat(mAppSearchImpl.getPackageToDatabases()) - .containsExactlyEntriesIn(existingDatabases); - } - - @Test - public void testPrunePackageData() throws AppSearchException { - List existingSchemas = - mAppSearchImpl.getSchemaProtoLocked().getTypesList(); - Map> existingDatabases = mAppSearchImpl.getPackageToDatabases(); - - Set existingPackages = new ArraySet<>(existingSchemas.size()); - for (int i = 0; i < existingSchemas.size(); i++) { - existingPackages.add(PrefixUtil.getPackageName(existingSchemas.get(i).getSchemaType())); - } - - // Insert schema for package A and B. - List schema = - ImmutableList.of(new AppSearchSchema.Builder("schema").build()); - mAppSearchImpl.setSchema( - "packageA", - "database", - schema, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - mAppSearchImpl.setSchema( - "packageB", - "database", - schema, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Verify these two packages is stored in AppSearch - SchemaProto expectedProto = - SchemaProto.newBuilder() - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("packageA$database/schema") - .setVersion(0)) - .addTypes( - SchemaTypeConfigProto.newBuilder() - .setSchemaType("packageB$database/schema") - .setVersion(0)) - .build(); - List expectedTypes = new ArrayList<>(); - expectedTypes.addAll(existingSchemas); - expectedTypes.addAll(expectedProto.getTypesList()); - assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) - .containsExactlyElementsIn(expectedTypes); - - // Prune packages - mAppSearchImpl.prunePackageData(existingPackages); - - // Verify the schema is same as beginning. - assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList()) - .containsExactlyElementsIn(existingSchemas); - assertThat(mAppSearchImpl.getPackageToDatabases()) - .containsExactlyEntriesIn(existingDatabases); - } - - @Test - public void testGetPackageToDatabases() throws Exception { - Map> existingMapping = mAppSearchImpl.getPackageToDatabases(); - Map> expectedMapping = new ArrayMap<>(); - expectedMapping.putAll(existingMapping); - - // Has database1 - expectedMapping.put("package1", ImmutableSet.of("database1")); - mAppSearchImpl.setSchema( - "package1", - "database1", - Collections.singletonList(new AppSearchSchema.Builder("schema").build()), - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - assertThat(mAppSearchImpl.getPackageToDatabases()) - .containsExactlyEntriesIn(expectedMapping); - - // Has both databases - expectedMapping.put("package1", ImmutableSet.of("database1", "database2")); - mAppSearchImpl.setSchema( - "package1", - "database2", - Collections.singletonList(new AppSearchSchema.Builder("schema").build()), - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - assertThat(mAppSearchImpl.getPackageToDatabases()) - .containsExactlyEntriesIn(expectedMapping); - - // Has both packages - expectedMapping.put("package2", ImmutableSet.of("database1")); - mAppSearchImpl.setSchema( - "package2", - "database1", - Collections.singletonList(new AppSearchSchema.Builder("schema").build()), - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - assertThat(mAppSearchImpl.getPackageToDatabases()) - .containsExactlyEntriesIn(expectedMapping); - } - - @Test - public void testRewriteSearchResultProto() throws Exception { - final String prefix = - "com.package.foo" - + PrefixUtil.PACKAGE_DELIMITER - + "databaseName" - + PrefixUtil.DATABASE_DELIMITER; - final String id = "id"; - final String namespace = prefix + "namespace"; - final String schemaType = prefix + "schema"; - - // Building the SearchResult received from query. - DocumentProto documentProto = - DocumentProto.newBuilder() - .setUri(id) - .setNamespace(namespace) - .setSchema(schemaType) - .build(); - SearchResultProto.ResultProto resultProto = - SearchResultProto.ResultProto.newBuilder().setDocument(documentProto).build(); - SearchResultProto searchResultProto = - SearchResultProto.newBuilder().addResults(resultProto).build(); - SchemaTypeConfigProto schemaTypeConfigProto = - SchemaTypeConfigProto.newBuilder().setSchemaType(schemaType).build(); - Map> schemaMap = - ImmutableMap.of(prefix, ImmutableMap.of(schemaType, schemaTypeConfigProto)); - - DocumentProto.Builder strippedDocumentProto = documentProto.toBuilder(); - removePrefixesFromDocument(strippedDocumentProto); - SearchResultPage searchResultPage = - AppSearchImpl.rewriteSearchResultProto(searchResultProto, schemaMap); - for (SearchResult result : searchResultPage.getResults()) { - assertThat(result.getPackageName()).isEqualTo("com.package.foo"); - assertThat(result.getDatabaseName()).isEqualTo("databaseName"); - assertThat(result.getGenericDocument()) - .isEqualTo( - GenericDocumentToProtoConverter.toGenericDocument( - strippedDocumentProto.build(), prefix, schemaMap.get(prefix))); - } - } - - @Test - public void testReportUsage() throws Exception { - // Insert schema - List 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 two docs - GenericDocument document1 = - new GenericDocument.Builder<>("namespace", "id1", "type").build(); - GenericDocument document2 = - new GenericDocument.Builder<>("namespace", "id2", "type").build(); - mAppSearchImpl.putDocument("package", "database", document1, /*logger=*/ null); - mAppSearchImpl.putDocument("package", "database", document2, /*logger=*/ null); - - // Report some usages. id1 has 2 app and 1 system usage, id2 has 1 app and 2 system usage. - mAppSearchImpl.reportUsage( - "package", - "database", - "namespace", - "id1", - /*usageTimestampMillis=*/ 10, - /*systemUsage=*/ false); - mAppSearchImpl.reportUsage( - "package", - "database", - "namespace", - "id1", - /*usageTimestampMillis=*/ 20, - /*systemUsage=*/ false); - mAppSearchImpl.reportUsage( - "package", - "database", - "namespace", - "id1", - /*usageTimestampMillis=*/ 1000, - /*systemUsage=*/ true); - - mAppSearchImpl.reportUsage( - "package", - "database", - "namespace", - "id2", - /*usageTimestampMillis=*/ 100, - /*systemUsage=*/ false); - mAppSearchImpl.reportUsage( - "package", - "database", - "namespace", - "id2", - /*usageTimestampMillis=*/ 200, - /*systemUsage=*/ true); - mAppSearchImpl.reportUsage( - "package", - "database", - "namespace", - "id2", - /*usageTimestampMillis=*/ 150, - /*systemUsage=*/ true); - - // Sort by app usage count: id1 should win - List page = - mAppSearchImpl - .query( - "package", - "database", - "", - new SearchSpec.Builder() - .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) - .setRankingStrategy(SearchSpec.RANKING_STRATEGY_USAGE_COUNT) - .build(), - /*logger=*/ null) - .getResults(); - assertThat(page).hasSize(2); - assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id1"); - assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id2"); - - // Sort by app usage timestamp: id2 should win - page = - mAppSearchImpl - .query( - "package", - "database", - "", - new SearchSpec.Builder() - .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) - .setRankingStrategy( - SearchSpec - .RANKING_STRATEGY_USAGE_LAST_USED_TIMESTAMP) - .build(), - /*logger=*/ null) - .getResults(); - assertThat(page).hasSize(2); - assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id2"); - assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id1"); - - // Sort by system usage count: id2 should win - page = - mAppSearchImpl - .query( - "package", - "database", - "", - new SearchSpec.Builder() - .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) - .setRankingStrategy( - SearchSpec.RANKING_STRATEGY_SYSTEM_USAGE_COUNT) - .build(), - /*logger=*/ null) - .getResults(); - assertThat(page).hasSize(2); - assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id2"); - assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id1"); - - // Sort by system usage timestamp: id1 should win - page = - mAppSearchImpl - .query( - "package", - "database", - "", - new SearchSpec.Builder() - .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) - .setRankingStrategy( - SearchSpec - .RANKING_STRATEGY_SYSTEM_USAGE_LAST_USED_TIMESTAMP) - .build(), - /*logger=*/ null) - .getResults(); - assertThat(page).hasSize(2); - assertThat(page.get(0).getGenericDocument().getId()).isEqualTo("id1"); - assertThat(page.get(1).getGenericDocument().getId()).isEqualTo("id2"); - } - - @Test - public void testGetStorageInfoForPackage_nonexistentPackage() throws Exception { - // "package2" doesn't exist yet, so it shouldn't have any storage size - StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForPackage("nonexistent.package"); - assertThat(storageInfo.getSizeBytes()).isEqualTo(0); - assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(0); - assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(0); - } - - @Test - public void testGetStorageInfoForPackage_withoutDocument() throws Exception { - // Insert schema for "package1" - List schemas = - Collections.singletonList(new AppSearchSchema.Builder("type").build()); - mAppSearchImpl.setSchema( - "package1", - "database", - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Since "package1" doesn't have a document, it get any space attributed to it. - StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForPackage("package1"); - assertThat(storageInfo.getSizeBytes()).isEqualTo(0); - assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(0); - assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(0); - } - - @Test - public void testGetStorageInfoForPackage_proportionalToDocuments() throws Exception { - List schemas = - Collections.singletonList(new AppSearchSchema.Builder("type").build()); - - // Insert schema for "package1" - mAppSearchImpl.setSchema( - "package1", - "database", - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert document for "package1" - GenericDocument document = - new GenericDocument.Builder<>("namespace", "id1", "type").build(); - mAppSearchImpl.putDocument("package1", "database", document, /*logger=*/ null); - - // Insert schema for "package2" - mAppSearchImpl.setSchema( - "package2", - "database", - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert two documents for "package2" - document = new GenericDocument.Builder<>("namespace", "id1", "type").build(); - mAppSearchImpl.putDocument("package2", "database", document, /*logger=*/ null); - document = new GenericDocument.Builder<>("namespace", "id2", "type").build(); - mAppSearchImpl.putDocument("package2", "database", document, /*logger=*/ null); - - StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForPackage("package1"); - long size1 = storageInfo.getSizeBytes(); - assertThat(size1).isGreaterThan(0); - assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(1); - assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(1); - - storageInfo = mAppSearchImpl.getStorageInfoForPackage("package2"); - long size2 = storageInfo.getSizeBytes(); - assertThat(size2).isGreaterThan(0); - assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(2); - assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(1); - - // Size is proportional to number of documents. Since "package2" has twice as many - // documents as "package1", its size is twice as much too. - assertThat(size2).isAtLeast(2 * size1); - } - - @Test - public void testGetStorageInfoForDatabase_nonexistentPackage() throws Exception { - // "package2" doesn't exist yet, so it shouldn't have any storage size - StorageInfo storageInfo = - mAppSearchImpl.getStorageInfoForDatabase( - "nonexistent.package", "nonexistentDatabase"); - assertThat(storageInfo.getSizeBytes()).isEqualTo(0); - assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(0); - assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(0); - } - - @Test - public void testGetStorageInfoForDatabase_nonexistentDatabase() throws Exception { - // Insert schema for "package1" - List schemas = - Collections.singletonList(new AppSearchSchema.Builder("type").build()); - mAppSearchImpl.setSchema( - "package1", - "database", - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // "package2" doesn't exist yet, so it shouldn't have any storage size - StorageInfo storageInfo = - mAppSearchImpl.getStorageInfoForDatabase("package1", "nonexistentDatabase"); - assertThat(storageInfo.getSizeBytes()).isEqualTo(0); - assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(0); - assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(0); - } - - @Test - public void testGetStorageInfoForDatabase_withoutDocument() throws Exception { - // Insert schema for "package1" - List 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); - - // Since "package1", "database1" doesn't have a document, it get any space attributed to it. - StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForDatabase("package1", "database1"); - assertThat(storageInfo.getSizeBytes()).isEqualTo(0); - assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(0); - assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(0); - } - - @Test - public void testGetStorageInfoForDatabase_proportionalToDocuments() throws Exception { - // Insert schema for "package1", "database1" and "database2" - List 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); - - // Add a document for "package1", "database1" - GenericDocument document = - new GenericDocument.Builder<>("namespace1", "id1", "type").build(); - mAppSearchImpl.putDocument("package1", "database1", document, /*logger=*/ null); - - // Add two documents for "package1", "database2" - document = new GenericDocument.Builder<>("namespace1", "id1", "type").build(); - mAppSearchImpl.putDocument("package1", "database2", document, /*logger=*/ null); - document = new GenericDocument.Builder<>("namespace1", "id2", "type").build(); - mAppSearchImpl.putDocument("package1", "database2", document, /*logger=*/ null); - - StorageInfo storageInfo = mAppSearchImpl.getStorageInfoForDatabase("package1", "database1"); - long size1 = storageInfo.getSizeBytes(); - assertThat(size1).isGreaterThan(0); - assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(1); - assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(1); - - storageInfo = mAppSearchImpl.getStorageInfoForDatabase("package1", "database2"); - long size2 = storageInfo.getSizeBytes(); - assertThat(size2).isGreaterThan(0); - assertThat(storageInfo.getAliveDocumentsCount()).isEqualTo(2); - assertThat(storageInfo.getAliveNamespacesCount()).isEqualTo(1); - - // Size is proportional to number of documents. Since "database2" has twice as many - // documents as "database1", its size is twice as much too. - assertThat(size2).isAtLeast(2 * size1); - } - - @Test - public void testThrowsExceptionIfClosed() throws Exception { - AppSearchImpl appSearchImpl = - AppSearchImpl.create( - mTemporaryFolder.newFolder(), - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - - // Initial check that we could do something at first. - List 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); - - appSearchImpl.close(); - - // Check all our public APIs - assertThrows( - IllegalStateException.class, - () -> - appSearchImpl.setSchema( - "package", - "database", - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0)); - - assertThrows( - IllegalStateException.class, () -> appSearchImpl.getSchema("package", "database")); - - assertThrows( - IllegalStateException.class, - () -> - appSearchImpl.putDocument( - "package", - "database", - new GenericDocument.Builder<>("namespace", "id", "type").build(), - /*logger=*/ null)); - - assertThrows( - IllegalStateException.class, - () -> - appSearchImpl.getDocument( - "package", "database", "namespace", "id", Collections.emptyMap())); - - assertThrows( - IllegalStateException.class, - () -> - appSearchImpl.query( - "package", - "database", - "query", - new SearchSpec.Builder().build(), - /*logger=*/ null)); - - assertThrows( - IllegalStateException.class, - () -> - appSearchImpl.globalQuery( - "query", - new SearchSpec.Builder().build(), - "package", - /*visibilityStore=*/ null, - Process.INVALID_UID, - /*callerHasSystemAccess=*/ false, - /*logger=*/ null)); - - assertThrows( - IllegalStateException.class, - () -> appSearchImpl.getNextPage("package", /*nextPageToken=*/ 1L)); - - assertThrows( - IllegalStateException.class, - () -> appSearchImpl.invalidateNextPageToken("package", /*nextPageToken=*/ 1L)); - - assertThrows( - IllegalStateException.class, - () -> - appSearchImpl.reportUsage( - "package", - "database", - "namespace", - "id", - /*usageTimestampMillis=*/ 1000L, - /*systemUsage=*/ false)); - - assertThrows( - IllegalStateException.class, - () -> - appSearchImpl.remove( - "package", - "database", - "namespace", - "id", - /*removeStatsBuilder=*/ null)); - - assertThrows( - IllegalStateException.class, - () -> - appSearchImpl.removeByQuery( - "package", - "database", - "query", - new SearchSpec.Builder().build(), - /*removeStatsBuilder=*/ null)); - - assertThrows( - IllegalStateException.class, - () -> appSearchImpl.getStorageInfoForPackage("package")); - - assertThrows( - IllegalStateException.class, - () -> appSearchImpl.getStorageInfoForDatabase("package", "database")); - - assertThrows( - IllegalStateException.class, - () -> appSearchImpl.persistToDisk(PersistType.Code.FULL)); - } - - @Test - public void testPutPersistsWithLiteFlush() throws Exception { - // Setup the index - File appsearchDir = mTemporaryFolder.newFolder(); - AppSearchImpl appSearchImpl = - AppSearchImpl.create( - appsearchDir, - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - - List 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 a document and persist it. - GenericDocument document = - new GenericDocument.Builder<>("namespace1", "id1", "type").build(); - appSearchImpl.putDocument("package", "database", document, /*logger=*/ null); - appSearchImpl.persistToDisk(PersistType.Code.LITE); - - GenericDocument getResult = - appSearchImpl.getDocument( - "package", "database", "namespace1", "id1", Collections.emptyMap()); - assertThat(getResult).isEqualTo(document); - - // That document should be visible even from another instance. - AppSearchImpl appSearchImpl2 = - AppSearchImpl.create( - appsearchDir, - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - getResult = - appSearchImpl2.getDocument( - "package", "database", "namespace1", "id1", Collections.emptyMap()); - assertThat(getResult).isEqualTo(document); - } - - @Test - public void testDeletePersistsWithLiteFlush() throws Exception { - // Setup the index - File appsearchDir = mTemporaryFolder.newFolder(); - AppSearchImpl appSearchImpl = - AppSearchImpl.create( - appsearchDir, - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - - List 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 and persist them. - 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); - appSearchImpl.persistToDisk(PersistType.Code.LITE); - - GenericDocument getResult = - appSearchImpl.getDocument( - "package", "database", "namespace1", "id1", Collections.emptyMap()); - assertThat(getResult).isEqualTo(document1); - getResult = - appSearchImpl.getDocument( - "package", "database", "namespace1", "id2", Collections.emptyMap()); - assertThat(getResult).isEqualTo(document2); - - // Delete the first document - appSearchImpl.remove("package", "database", "namespace1", "id1", /*statsBuilder=*/ null); - appSearchImpl.persistToDisk(PersistType.Code.LITE); - assertThrows( - AppSearchException.class, - () -> - appSearchImpl.getDocument( - "package", - "database", - "namespace1", - "id1", - Collections.emptyMap())); - getResult = - appSearchImpl.getDocument( - "package", "database", "namespace1", "id2", Collections.emptyMap()); - assertThat(getResult).isEqualTo(document2); - - // Only the second document should be retrievable from another instance. - AppSearchImpl appSearchImpl2 = - AppSearchImpl.create( - appsearchDir, - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - assertThrows( - AppSearchException.class, - () -> - appSearchImpl2.getDocument( - "package", - "database", - "namespace1", - "id1", - Collections.emptyMap())); - getResult = - appSearchImpl2.getDocument( - "package", "database", "namespace1", "id2", Collections.emptyMap()); - assertThat(getResult).isEqualTo(document2); - } - - @Test - public void testDeleteByQueryPersistsWithLiteFlush() throws Exception { - // Setup the index - File appsearchDir = mTemporaryFolder.newFolder(); - AppSearchImpl appSearchImpl = - AppSearchImpl.create( - appsearchDir, - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - - List 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 and persist them. - GenericDocument document1 = - new GenericDocument.Builder<>("namespace1", "id1", "type").build(); - appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null); - GenericDocument document2 = - new GenericDocument.Builder<>("namespace2", "id2", "type").build(); - appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null); - appSearchImpl.persistToDisk(PersistType.Code.LITE); - - GenericDocument getResult = - appSearchImpl.getDocument( - "package", "database", "namespace1", "id1", Collections.emptyMap()); - assertThat(getResult).isEqualTo(document1); - getResult = - appSearchImpl.getDocument( - "package", "database", "namespace2", "id2", Collections.emptyMap()); - assertThat(getResult).isEqualTo(document2); - - // Delete the first document - appSearchImpl.removeByQuery( - "package", - "database", - "", - new SearchSpec.Builder() - .addFilterNamespaces("namespace1") - .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) - .build(), - /*statsBuilder=*/ null); - appSearchImpl.persistToDisk(PersistType.Code.LITE); - assertThrows( - AppSearchException.class, - () -> - appSearchImpl.getDocument( - "package", - "database", - "namespace1", - "id1", - Collections.emptyMap())); - getResult = - appSearchImpl.getDocument( - "package", "database", "namespace2", "id2", Collections.emptyMap()); - assertThat(getResult).isEqualTo(document2); - - // Only the second document should be retrievable from another instance. - AppSearchImpl appSearchImpl2 = - AppSearchImpl.create( - appsearchDir, - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - assertThrows( - AppSearchException.class, - () -> - appSearchImpl2.getDocument( - "package", - "database", - "namespace1", - "id1", - Collections.emptyMap())); - getResult = - appSearchImpl2.getDocument( - "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 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 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 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 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 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 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 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 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 deleted file mode 100644 index 7c976876a7317..0000000000000 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java +++ /dev/null @@ -1,806 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage; - -import static com.google.common.truth.Truth.assertThat; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.appsearch.AppSearchResult; -import android.app.appsearch.AppSearchSchema; -import android.app.appsearch.GenericDocument; -import android.app.appsearch.SearchResultPage; -import android.app.appsearch.SearchSpec; -import android.app.appsearch.exceptions.AppSearchException; - -import com.android.server.appsearch.external.localstorage.stats.CallStats; -import com.android.server.appsearch.external.localstorage.stats.InitializeStats; -import com.android.server.appsearch.external.localstorage.stats.OptimizeStats; -import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; -import com.android.server.appsearch.external.localstorage.stats.RemoveStats; -import com.android.server.appsearch.external.localstorage.stats.SearchStats; -import com.android.server.appsearch.icing.proto.DeleteStatsProto; -import com.android.server.appsearch.icing.proto.DocumentProto; -import com.android.server.appsearch.icing.proto.InitializeStatsProto; -import com.android.server.appsearch.icing.proto.OptimizeStatsProto; -import com.android.server.appsearch.icing.proto.PutDocumentStatsProto; -import com.android.server.appsearch.icing.proto.PutResultProto; -import com.android.server.appsearch.icing.proto.QueryStatsProto; -import com.android.server.appsearch.icing.proto.ScoringSpecProto; -import com.android.server.appsearch.icing.proto.StatusProto; -import com.android.server.appsearch.icing.proto.TermMatchType; - -import com.google.common.collect.ImmutableList; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import java.io.File; -import java.util.Collections; -import java.util.List; - -public class AppSearchLoggerTest { - @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); - private AppSearchImpl mAppSearchImpl; - private TestLogger mLogger; - /** - * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class. - */ - private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true; - - @Before - public void setUp() throws Exception { - mAppSearchImpl = - AppSearchImpl.create( - mTemporaryFolder.newFolder(), - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - mLogger = new TestLogger(); - } - - // Test only not thread safe. - public static class TestLogger implements AppSearchLogger { - @Nullable CallStats mCallStats; - @Nullable PutDocumentStats mPutDocumentStats; - @Nullable InitializeStats mInitializeStats; - @Nullable SearchStats mSearchStats; - @Nullable RemoveStats mRemoveStats; - @Nullable OptimizeStats mOptimizeStats; - - @Override - public void logStats(@NonNull CallStats stats) { - mCallStats = stats; - } - - @Override - public void logStats(@NonNull PutDocumentStats stats) { - mPutDocumentStats = stats; - } - - @Override - public void logStats(@NonNull InitializeStats stats) { - mInitializeStats = stats; - } - - @Override - public void logStats(@NonNull SearchStats stats) { - mSearchStats = stats; - } - - @Override - public void logStats(@NonNull RemoveStats stats) { - mRemoveStats = stats; - } - - @Override - public void logStats(@NonNull OptimizeStats stats) { - mOptimizeStats = stats; - } - } - - @Test - public void testAppSearchLoggerHelper_testCopyNativeStats_initialize() { - int nativeLatencyMillis = 3; - int nativeDocumentStoreRecoveryCause = InitializeStatsProto.RecoveryCause.DATA_LOSS_VALUE; - int nativeIndexRestorationCause = - InitializeStatsProto.RecoveryCause.INCONSISTENT_WITH_GROUND_TRUTH_VALUE; - int nativeSchemaStoreRecoveryCause = - InitializeStatsProto.RecoveryCause.SCHEMA_CHANGES_OUT_OF_SYNC_VALUE; - int nativeDocumentStoreRecoveryLatencyMillis = 7; - int nativeIndexRestorationLatencyMillis = 8; - int nativeSchemaStoreRecoveryLatencyMillis = 9; - int nativeDocumentStoreDataStatus = - InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE; - int nativeNumDocuments = 11; - int nativeNumSchemaTypes = 12; - InitializeStatsProto.Builder nativeInitBuilder = - InitializeStatsProto.newBuilder() - .setLatencyMs(nativeLatencyMillis) - .setDocumentStoreRecoveryCause( - InitializeStatsProto.RecoveryCause.forNumber( - nativeDocumentStoreRecoveryCause)) - .setIndexRestorationCause( - InitializeStatsProto.RecoveryCause.forNumber( - nativeIndexRestorationCause)) - .setSchemaStoreRecoveryCause( - InitializeStatsProto.RecoveryCause.forNumber( - nativeSchemaStoreRecoveryCause)) - .setDocumentStoreRecoveryLatencyMs(nativeDocumentStoreRecoveryLatencyMillis) - .setIndexRestorationLatencyMs(nativeIndexRestorationLatencyMillis) - .setSchemaStoreRecoveryLatencyMs(nativeSchemaStoreRecoveryLatencyMillis) - .setDocumentStoreDataStatus( - InitializeStatsProto.DocumentStoreDataStatus.forNumber( - nativeDocumentStoreDataStatus)) - .setNumDocuments(nativeNumDocuments) - .setNumSchemaTypes(nativeNumSchemaTypes); - InitializeStats.Builder initBuilder = new InitializeStats.Builder(); - - AppSearchLoggerHelper.copyNativeStats(nativeInitBuilder.build(), initBuilder); - - InitializeStats iStats = initBuilder.build(); - assertThat(iStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); - assertThat(iStats.getDocumentStoreRecoveryCause()) - .isEqualTo(nativeDocumentStoreRecoveryCause); - assertThat(iStats.getIndexRestorationCause()).isEqualTo(nativeIndexRestorationCause); - assertThat(iStats.getSchemaStoreRecoveryCause()).isEqualTo(nativeSchemaStoreRecoveryCause); - assertThat(iStats.getDocumentStoreRecoveryLatencyMillis()) - .isEqualTo(nativeDocumentStoreRecoveryLatencyMillis); - assertThat(iStats.getIndexRestorationLatencyMillis()) - .isEqualTo(nativeIndexRestorationLatencyMillis); - assertThat(iStats.getSchemaStoreRecoveryLatencyMillis()) - .isEqualTo(nativeSchemaStoreRecoveryLatencyMillis); - assertThat(iStats.getDocumentStoreDataStatus()).isEqualTo(nativeDocumentStoreDataStatus); - assertThat(iStats.getDocumentCount()).isEqualTo(nativeNumDocuments); - assertThat(iStats.getSchemaTypeCount()).isEqualTo(nativeNumSchemaTypes); - } - - @Test - public void testAppSearchLoggerHelper_testCopyNativeStats_putDocument() { - final int nativeLatencyMillis = 3; - final int nativeDocumentStoreLatencyMillis = 4; - final int nativeIndexLatencyMillis = 5; - final int nativeIndexMergeLatencyMillis = 6; - final int nativeDocumentSize = 7; - final int nativeNumTokensIndexed = 8; - final boolean nativeExceededMaxNumTokens = true; - PutDocumentStatsProto nativePutDocumentStats = - PutDocumentStatsProto.newBuilder() - .setLatencyMs(nativeLatencyMillis) - .setDocumentStoreLatencyMs(nativeDocumentStoreLatencyMillis) - .setIndexLatencyMs(nativeIndexLatencyMillis) - .setIndexMergeLatencyMs(nativeIndexMergeLatencyMillis) - .setDocumentSize(nativeDocumentSize) - .setTokenizationStats( - PutDocumentStatsProto.TokenizationStats.newBuilder() - .setNumTokensIndexed(nativeNumTokensIndexed) - .setExceededMaxTokenNum(nativeExceededMaxNumTokens) - .build()) - .build(); - PutDocumentStats.Builder pBuilder = new PutDocumentStats.Builder("packageName", "database"); - - AppSearchLoggerHelper.copyNativeStats(nativePutDocumentStats, pBuilder); - - PutDocumentStats pStats = pBuilder.build(); - assertThat(pStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); - assertThat(pStats.getNativeDocumentStoreLatencyMillis()) - .isEqualTo(nativeDocumentStoreLatencyMillis); - assertThat(pStats.getNativeIndexLatencyMillis()).isEqualTo(nativeIndexLatencyMillis); - assertThat(pStats.getNativeIndexMergeLatencyMillis()) - .isEqualTo(nativeIndexMergeLatencyMillis); - assertThat(pStats.getNativeDocumentSizeBytes()).isEqualTo(nativeDocumentSize); - assertThat(pStats.getNativeNumTokensIndexed()).isEqualTo(nativeNumTokensIndexed); - assertThat(pStats.getNativeExceededMaxNumTokens()).isEqualTo(nativeExceededMaxNumTokens); - } - - @Test - public void testAppSearchLoggerHelper_testCopyNativeStats_search() { - int nativeLatencyMillis = 4; - int nativeNumTerms = 5; - int nativeQueryLength = 6; - int nativeNumNamespacesFiltered = 7; - int nativeNumSchemaTypesFiltered = 8; - int nativeRequestedPageSize = 9; - int nativeNumResultsReturnedCurrentPage = 10; - boolean nativeIsFirstPage = true; - int nativeParseQueryLatencyMillis = 11; - int nativeRankingStrategy = ScoringSpecProto.RankingStrategy.Code.CREATION_TIMESTAMP_VALUE; - int nativeNumDocumentsScored = 13; - int nativeScoringLatencyMillis = 14; - int nativeRankingLatencyMillis = 15; - int nativeNumResultsWithSnippets = 16; - int nativeDocumentRetrievingLatencyMillis = 17; - QueryStatsProto nativeQueryStats = - QueryStatsProto.newBuilder() - .setLatencyMs(nativeLatencyMillis) - .setNumTerms(nativeNumTerms) - .setQueryLength(nativeQueryLength) - .setNumNamespacesFiltered(nativeNumNamespacesFiltered) - .setNumSchemaTypesFiltered(nativeNumSchemaTypesFiltered) - .setRequestedPageSize(nativeRequestedPageSize) - .setNumResultsReturnedCurrentPage(nativeNumResultsReturnedCurrentPage) - .setIsFirstPage(nativeIsFirstPage) - .setParseQueryLatencyMs(nativeParseQueryLatencyMillis) - .setRankingStrategy( - ScoringSpecProto.RankingStrategy.Code.forNumber( - nativeRankingStrategy)) - .setNumDocumentsScored(nativeNumDocumentsScored) - .setScoringLatencyMs(nativeScoringLatencyMillis) - .setRankingLatencyMs(nativeRankingLatencyMillis) - .setNumResultsWithSnippets(nativeNumResultsWithSnippets) - .setDocumentRetrievalLatencyMs(nativeDocumentRetrievingLatencyMillis) - .build(); - SearchStats.Builder qBuilder = - new SearchStats.Builder(SearchStats.VISIBILITY_SCOPE_LOCAL, "packageName") - .setDatabase("database"); - - AppSearchLoggerHelper.copyNativeStats(nativeQueryStats, qBuilder); - - SearchStats sStats = qBuilder.build(); - assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); - assertThat(sStats.getTermCount()).isEqualTo(nativeNumTerms); - assertThat(sStats.getQueryLength()).isEqualTo(nativeQueryLength); - assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(nativeNumNamespacesFiltered); - assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(nativeNumSchemaTypesFiltered); - assertThat(sStats.getRequestedPageSize()).isEqualTo(nativeRequestedPageSize); - assertThat(sStats.getCurrentPageReturnedResultCount()) - .isEqualTo(nativeNumResultsReturnedCurrentPage); - assertThat(sStats.isFirstPage()).isTrue(); - assertThat(sStats.getParseQueryLatencyMillis()).isEqualTo(nativeParseQueryLatencyMillis); - assertThat(sStats.getRankingStrategy()).isEqualTo(nativeRankingStrategy); - assertThat(sStats.getScoredDocumentCount()).isEqualTo(nativeNumDocumentsScored); - assertThat(sStats.getScoringLatencyMillis()).isEqualTo(nativeScoringLatencyMillis); - assertThat(sStats.getRankingLatencyMillis()).isEqualTo(nativeRankingLatencyMillis); - assertThat(sStats.getResultWithSnippetsCount()).isEqualTo(nativeNumResultsWithSnippets); - assertThat(sStats.getDocumentRetrievingLatencyMillis()) - .isEqualTo(nativeDocumentRetrievingLatencyMillis); - } - - @Test - public void testAppSearchLoggerHelper_testCopyNativeStats_remove() { - final int nativeLatencyMillis = 1; - final int nativeDeleteType = 2; - final int nativeNumDocumentDeleted = 3; - DeleteStatsProto nativeDeleteStatsProto = - DeleteStatsProto.newBuilder() - .setLatencyMs(nativeLatencyMillis) - .setDeleteType(DeleteStatsProto.DeleteType.Code.forNumber(nativeDeleteType)) - .setNumDocumentsDeleted(nativeNumDocumentDeleted) - .build(); - RemoveStats.Builder rBuilder = new RemoveStats.Builder("packageName", "database"); - - AppSearchLoggerHelper.copyNativeStats(nativeDeleteStatsProto, rBuilder); - - RemoveStats rStats = rBuilder.build(); - assertThat(rStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); - assertThat(rStats.getDeleteType()).isEqualTo(nativeDeleteType); - assertThat(rStats.getDeletedDocumentCount()).isEqualTo(nativeNumDocumentDeleted); - } - - @Test - public void testAppSearchLoggerHelper_testCopyNativeStats_optimize() { - int nativeLatencyMillis = 1; - int nativeDocumentStoreOptimizeLatencyMillis = 2; - int nativeIndexRestorationLatencyMillis = 3; - int nativeNumOriginalDocuments = 4; - int nativeNumDeletedDocuments = 5; - int nativeNumExpiredDocuments = 6; - long nativeStorageSizeBeforeBytes = Integer.MAX_VALUE + 1; - long nativeStorageSizeAfterBytes = Integer.MAX_VALUE + 2; - long nativeTimeSinceLastOptimizeMillis = Integer.MAX_VALUE + 3; - OptimizeStatsProto optimizeStatsProto = - OptimizeStatsProto.newBuilder() - .setLatencyMs(nativeLatencyMillis) - .setDocumentStoreOptimizeLatencyMs(nativeDocumentStoreOptimizeLatencyMillis) - .setIndexRestorationLatencyMs(nativeIndexRestorationLatencyMillis) - .setNumOriginalDocuments(nativeNumOriginalDocuments) - .setNumDeletedDocuments(nativeNumDeletedDocuments) - .setNumExpiredDocuments(nativeNumExpiredDocuments) - .setStorageSizeBefore(nativeStorageSizeBeforeBytes) - .setStorageSizeAfter(nativeStorageSizeAfterBytes) - .setTimeSinceLastOptimizeMs(nativeTimeSinceLastOptimizeMillis) - .build(); - OptimizeStats.Builder oBuilder = new OptimizeStats.Builder(); - - AppSearchLoggerHelper.copyNativeStats(optimizeStatsProto, oBuilder); - - OptimizeStats oStats = oBuilder.build(); - assertThat(oStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); - assertThat(oStats.getDocumentStoreOptimizeLatencyMillis()) - .isEqualTo(nativeDocumentStoreOptimizeLatencyMillis); - assertThat(oStats.getIndexRestorationLatencyMillis()) - .isEqualTo(nativeIndexRestorationLatencyMillis); - assertThat(oStats.getOriginalDocumentCount()).isEqualTo(nativeNumOriginalDocuments); - assertThat(oStats.getDeletedDocumentCount()).isEqualTo(nativeNumDeletedDocuments); - assertThat(oStats.getExpiredDocumentCount()).isEqualTo(nativeNumExpiredDocuments); - assertThat(oStats.getStorageSizeBeforeBytes()).isEqualTo(nativeStorageSizeBeforeBytes); - assertThat(oStats.getStorageSizeAfterBytes()).isEqualTo(nativeStorageSizeAfterBytes); - assertThat(oStats.getTimeSinceLastOptimizeMillis()) - .isEqualTo(nativeTimeSinceLastOptimizeMillis); - } - - // - // Testing actual logging - // - @Test - 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(), - new UnlimitedLimitConfig(), - initStatsBuilder, - ALWAYS_OPTIMIZE); - InitializeStats iStats = initStatsBuilder.build(); - - assertThat(iStats).isNotNull(); - assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK); - // Total latency captured in LocalStorage - assertThat(iStats.getTotalLatencyMillis()).isEqualTo(0); - assertThat(iStats.hasDeSync()).isFalse(); - assertThat(iStats.getNativeLatencyMillis()).isGreaterThan(0); - assertThat(iStats.getDocumentStoreDataStatus()) - .isEqualTo(InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE); - assertThat(iStats.getDocumentCount()).isEqualTo(0); - assertThat(iStats.getSchemaTypeCount()).isEqualTo(0); - assertThat(iStats.hasReset()).isEqualTo(false); - assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_OK); - } - - @Test - public void testLoggingStats_initializeWithDocuments_success() throws Exception { - final String testPackageName = "testPackage"; - final String testDatabase = "testDatabase"; - final File folder = mTemporaryFolder.newFolder(); - - AppSearchImpl appSearchImpl = - AppSearchImpl.create( - folder, - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - List schemas = - ImmutableList.of( - new AppSearchSchema.Builder("Type1").build(), - new AppSearchSchema.Builder("Type2").build()); - appSearchImpl.setSchema( - testPackageName, - testDatabase, - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type1").build(); - GenericDocument doc2 = new GenericDocument.Builder<>("namespace", "id2", "Type1").build(); - appSearchImpl.putDocument(testPackageName, testDatabase, doc1, mLogger); - appSearchImpl.putDocument(testPackageName, testDatabase, doc2, mLogger); - appSearchImpl.close(); - - // Create another appsearchImpl on the same folder - InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder(); - AppSearchImpl.create(folder, new UnlimitedLimitConfig(), initStatsBuilder, ALWAYS_OPTIMIZE); - InitializeStats iStats = initStatsBuilder.build(); - - assertThat(iStats).isNotNull(); - assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK); - // Total latency captured in LocalStorage - assertThat(iStats.getTotalLatencyMillis()).isEqualTo(0); - assertThat(iStats.hasDeSync()).isFalse(); - assertThat(iStats.getNativeLatencyMillis()).isGreaterThan(0); - assertThat(iStats.getDocumentStoreDataStatus()) - .isEqualTo(InitializeStatsProto.DocumentStoreDataStatus.NO_DATA_LOSS_VALUE); - assertThat(iStats.getDocumentCount()).isEqualTo(2); - assertThat(iStats.getSchemaTypeCount()).isEqualTo(2); - assertThat(iStats.hasReset()).isEqualTo(false); - assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_OK); - } - - @Test - public void testLoggingStats_initialize_failure() throws Exception { - final String testPackageName = "testPackage"; - final String testDatabase = "testDatabase"; - final File folder = mTemporaryFolder.newFolder(); - - AppSearchImpl appSearchImpl = - AppSearchImpl.create( - folder, - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - - List schemas = - ImmutableList.of( - new AppSearchSchema.Builder("Type1").build(), - new AppSearchSchema.Builder("Type2").build()); - appSearchImpl.setSchema( - testPackageName, - testDatabase, - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - // Insert a valid doc - GenericDocument doc1 = new GenericDocument.Builder<>("namespace", "id1", "Type1").build(); - appSearchImpl.putDocument(testPackageName, testDatabase, doc1, mLogger); - - // Insert the invalid doc with an invalid namespace right into icing - DocumentProto invalidDoc = - DocumentProto.newBuilder() - .setNamespace("invalidNamespace") - .setUri("id2") - .setSchema(String.format("%s$%s/Type1", testPackageName, testDatabase)) - .build(); - PutResultProto putResultProto = appSearchImpl.mIcingSearchEngineLocked.put(invalidDoc); - assertThat(putResultProto.getStatus().getCode()).isEqualTo(StatusProto.Code.OK); - appSearchImpl.close(); - - // Create another appsearchImpl on the same folder - InitializeStats.Builder initStatsBuilder = new InitializeStats.Builder(); - AppSearchImpl.create(folder, new UnlimitedLimitConfig(), initStatsBuilder, ALWAYS_OPTIMIZE); - InitializeStats iStats = initStatsBuilder.build(); - - // Some of other fields are already covered by AppSearchImplTest#testReset() - assertThat(iStats).isNotNull(); - assertThat(iStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_INTERNAL_ERROR); - assertThat(iStats.hasReset()).isTrue(); - } - - @Test - public void testLoggingStats_putDocument_success() throws Exception { - // Insert schema - final String testPackageName = "testPackage"; - final String testDatabase = "testDatabase"; - AppSearchSchema testSchema = - new AppSearchSchema.Builder("type") - .addProperty( - new AppSearchSchema.StringPropertyConfig.Builder("subject") - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .setIndexingType( - AppSearchSchema.StringPropertyConfig - .INDEXING_TYPE_PREFIXES) - .setTokenizerType( - AppSearchSchema.StringPropertyConfig - .TOKENIZER_TYPE_PLAIN) - .build()) - .build(); - List schemas = Collections.singletonList(testSchema); - mAppSearchImpl.setSchema( - testPackageName, - testDatabase, - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - GenericDocument document = - new GenericDocument.Builder<>("namespace", "id", "type") - .setPropertyString("subject", "testPut example1") - .build(); - - mAppSearchImpl.putDocument(testPackageName, testDatabase, document, mLogger); - - PutDocumentStats pStats = mLogger.mPutDocumentStats; - assertThat(pStats).isNotNull(); - assertThat(pStats.getPackageName()).isEqualTo(testPackageName); - assertThat(pStats.getDatabase()).isEqualTo(testDatabase); - assertThat(pStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK); - // The latency related native stats have been tested in testCopyNativeStats - assertThat(pStats.getNativeDocumentSizeBytes()).isGreaterThan(0); - assertThat(pStats.getNativeNumTokensIndexed()).isGreaterThan(0); - } - - @Test - public void testLoggingStats_putDocument_failure() throws Exception { - // Insert schema - final String testPackageName = "testPackage"; - final String testDatabase = "testDatabase"; - AppSearchSchema testSchema = - new AppSearchSchema.Builder("type") - .addProperty( - new AppSearchSchema.StringPropertyConfig.Builder("subject") - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .setIndexingType( - AppSearchSchema.StringPropertyConfig - .INDEXING_TYPE_PREFIXES) - .setTokenizerType( - AppSearchSchema.StringPropertyConfig - .TOKENIZER_TYPE_PLAIN) - .build()) - .build(); - List schemas = Collections.singletonList(testSchema); - mAppSearchImpl.setSchema( - testPackageName, - testDatabase, - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - GenericDocument document = - new GenericDocument.Builder<>("namespace", "id", "type") - .setPropertyString("nonExist", "testPut example1") - .build(); - - 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(); - assertThat(pStats.getPackageName()).isEqualTo(testPackageName); - assertThat(pStats.getDatabase()).isEqualTo(testDatabase); - assertThat(pStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND); - } - - @Test - public void testLoggingStats_search_success() throws Exception { - // Insert schema - final String testPackageName = "testPackage"; - final String testDatabase = "testDatabase"; - AppSearchSchema testSchema = - new AppSearchSchema.Builder("type") - .addProperty( - new AppSearchSchema.StringPropertyConfig.Builder("subject") - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .setIndexingType( - AppSearchSchema.StringPropertyConfig - .INDEXING_TYPE_PREFIXES) - .setTokenizerType( - AppSearchSchema.StringPropertyConfig - .TOKENIZER_TYPE_PLAIN) - .build()) - .build(); - List schemas = Collections.singletonList(testSchema); - mAppSearchImpl.setSchema( - testPackageName, - testDatabase, - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - GenericDocument document1 = - new GenericDocument.Builder<>("namespace", "id1", "type") - .setPropertyString("subject", "testPut example1") - .build(); - GenericDocument document2 = - new GenericDocument.Builder<>("namespace", "id2", "type") - .setPropertyString("subject", "testPut example2") - .build(); - GenericDocument document3 = - new GenericDocument.Builder<>("namespace", "id3", "type") - .setPropertyString("subject", "testPut 3") - .build(); - mAppSearchImpl.putDocument(testPackageName, testDatabase, document1, mLogger); - mAppSearchImpl.putDocument(testPackageName, testDatabase, document2, mLogger); - mAppSearchImpl.putDocument(testPackageName, testDatabase, document3, mLogger); - - // No query filters specified. package2 should only get its own documents back. - SearchSpec searchSpec = - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .setRankingStrategy(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP) - .build(); - String queryStr = "testPut e"; - SearchResultPage searchResultPage = - mAppSearchImpl.query( - testPackageName, testDatabase, queryStr, searchSpec, /*logger=*/ mLogger); - - assertThat(searchResultPage.getResults()).hasSize(2); - // The ranking strategy is LIFO - assertThat(searchResultPage.getResults().get(0).getGenericDocument()).isEqualTo(document2); - assertThat(searchResultPage.getResults().get(1).getGenericDocument()).isEqualTo(document1); - - SearchStats sStats = mLogger.mSearchStats; - - assertThat(sStats).isNotNull(); - assertThat(sStats.getPackageName()).isEqualTo(testPackageName); - assertThat(sStats.getDatabase()).isEqualTo(testDatabase); - assertThat(sStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK); - assertThat(sStats.getTotalLatencyMillis()).isGreaterThan(0); - assertThat(sStats.getVisibilityScope()).isEqualTo(SearchStats.VISIBILITY_SCOPE_LOCAL); - assertThat(sStats.getTermCount()).isEqualTo(2); - assertThat(sStats.getQueryLength()).isEqualTo(queryStr.length()); - assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(1); - assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(1); - assertThat(sStats.getCurrentPageReturnedResultCount()).isEqualTo(2); - assertThat(sStats.isFirstPage()).isTrue(); - assertThat(sStats.getRankingStrategy()) - .isEqualTo(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP); - assertThat(sStats.getScoredDocumentCount()).isEqualTo(2); - assertThat(sStats.getResultWithSnippetsCount()).isEqualTo(0); - } - - @Test - public void testLoggingStats_search_failure() throws Exception { - final String testPackageName = "testPackage"; - final String testDatabase = "testDatabase"; - List schemas = - ImmutableList.of( - new AppSearchSchema.Builder("Type1").build(), - new AppSearchSchema.Builder("Type2").build()); - mAppSearchImpl.setSchema( - testPackageName, - testDatabase, - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - SearchSpec searchSpec = - new SearchSpec.Builder() - .setTermMatch(TermMatchType.Code.PREFIX_VALUE) - .setRankingStrategy(SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP) - .addFilterPackageNames("anotherPackage") - .build(); - - mAppSearchImpl.query( - testPackageName, - testPackageName, - /* queryExpression= */ "", - searchSpec, - /*logger=*/ mLogger); - - SearchStats sStats = mLogger.mSearchStats; - assertThat(sStats).isNotNull(); - assertThat(sStats.getPackageName()).isEqualTo(testPackageName); - assertThat(sStats.getDatabase()).isEqualTo(testPackageName); - assertThat(sStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_SECURITY_ERROR); - } - - @Test - public void testLoggingStats_remove_success() throws Exception { - // Insert schema - final String testPackageName = "testPackage"; - final String testDatabase = "testDatabase"; - final String testNamespace = "testNameSpace"; - final String testId = "id"; - List schemas = - Collections.singletonList(new AppSearchSchema.Builder("type").build()); - mAppSearchImpl.setSchema( - testPackageName, - testDatabase, - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - GenericDocument document = - new GenericDocument.Builder<>(testNamespace, testId, "type").build(); - mAppSearchImpl.putDocument(testPackageName, testDatabase, document, /*logger=*/ null); - - RemoveStats.Builder rStatsBuilder = new RemoveStats.Builder(testPackageName, testDatabase); - mAppSearchImpl.remove(testPackageName, testDatabase, testNamespace, testId, rStatsBuilder); - RemoveStats rStats = rStatsBuilder.build(); - - assertThat(rStats.getPackageName()).isEqualTo(testPackageName); - assertThat(rStats.getDatabase()).isEqualTo(testDatabase); - // delete by namespace + id - assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK); - assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.SINGLE_VALUE); - assertThat(rStats.getDeletedDocumentCount()).isEqualTo(1); - } - - @Test - public void testLoggingStats_remove_failure() throws Exception { - // Insert schema - final String testPackageName = "testPackage"; - final String testDatabase = "testDatabase"; - final String testNamespace = "testNameSpace"; - final String testId = "id"; - List schemas = - Collections.singletonList(new AppSearchSchema.Builder("type").build()); - mAppSearchImpl.setSchema( - testPackageName, - testDatabase, - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - - GenericDocument document = - new GenericDocument.Builder<>(testNamespace, testId, "type").build(); - mAppSearchImpl.putDocument(testPackageName, testDatabase, document, /*logger=*/ null); - - RemoveStats.Builder rStatsBuilder = new RemoveStats.Builder(testPackageName, testDatabase); - - 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); - assertThat(rStats.getDatabase()).isEqualTo(testDatabase); - assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_NOT_FOUND); - // delete by namespace + id - assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.SINGLE_VALUE); - assertThat(rStats.getDeletedDocumentCount()).isEqualTo(0); - } - - @Test - public void testLoggingStats_removeByQuery_success() throws Exception { - // Insert schema - final String testPackageName = "testPackage"; - final String testDatabase = "testDatabase"; - final String testNamespace = "testNameSpace"; - List schemas = - Collections.singletonList(new AppSearchSchema.Builder("type").build()); - mAppSearchImpl.setSchema( - testPackageName, - testDatabase, - schemas, - /*visibilityStore=*/ null, - /*schemasNotDisplayedBySystem=*/ Collections.emptyList(), - /*schemasVisibleToPackages=*/ Collections.emptyMap(), - /*forceOverride=*/ false, - /*version=*/ 0); - GenericDocument document1 = - new GenericDocument.Builder<>(testNamespace, "id1", "type").build(); - GenericDocument document2 = - new GenericDocument.Builder<>(testNamespace, "id2", "type").build(); - mAppSearchImpl.putDocument(testPackageName, testDatabase, document1, mLogger); - mAppSearchImpl.putDocument(testPackageName, testDatabase, document2, mLogger); - // No query filters specified. package2 should only get its own documents back. - SearchSpec searchSpec = - new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build(); - - RemoveStats.Builder rStatsBuilder = new RemoveStats.Builder(testPackageName, testDatabase); - mAppSearchImpl.removeByQuery( - testPackageName, testDatabase, /*queryExpression=*/ "", searchSpec, rStatsBuilder); - RemoveStats rStats = rStatsBuilder.build(); - - assertThat(rStats.getPackageName()).isEqualTo(testPackageName); - assertThat(rStats.getDatabase()).isEqualTo(testDatabase); - assertThat(rStats.getStatusCode()).isEqualTo(AppSearchResult.RESULT_OK); - // delete by query - assertThat(rStats.getDeleteType()).isEqualTo(DeleteStatsProto.DeleteType.Code.QUERY_VALUE); - assertThat(rStats.getDeletedDocumentCount()).isEqualTo(2); - } -} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java deleted file mode 100644 index 204fc54fab5b0..0000000000000 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/GenericDocumentToProtoConverterTest.java +++ /dev/null @@ -1,256 +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 com.android.server.appsearch.external.localstorage.converter; - -import static com.google.common.truth.Truth.assertThat; - -import android.app.appsearch.GenericDocument; - -import com.android.server.appsearch.icing.proto.DocumentProto; -import com.android.server.appsearch.icing.proto.PropertyConfigProto; -import com.android.server.appsearch.icing.proto.PropertyProto; -import com.android.server.appsearch.icing.proto.SchemaTypeConfigProto; -import com.android.server.appsearch.protobuf.ByteString; - -import com.google.common.collect.ImmutableMap; - -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class GenericDocumentToProtoConverterTest { - private static final byte[] BYTE_ARRAY_1 = new byte[] {(byte) 1, (byte) 2, (byte) 3}; - private static final byte[] BYTE_ARRAY_2 = new byte[] {(byte) 4, (byte) 5, (byte) 6, (byte) 7}; - private static final String SCHEMA_TYPE_1 = "sDocumentPropertiesSchemaType1"; - private static final String SCHEMA_TYPE_2 = "sDocumentPropertiesSchemaType2"; - private static final GenericDocument DOCUMENT_PROPERTIES_1 = - new GenericDocument.Builder>( - "namespace", "sDocumentProperties1", SCHEMA_TYPE_1) - .setCreationTimestampMillis(12345L) - .build(); - private static final GenericDocument DOCUMENT_PROPERTIES_2 = - new GenericDocument.Builder>( - "namespace", "sDocumentProperties2", SCHEMA_TYPE_2) - .setCreationTimestampMillis(6789L) - .build(); - private static final SchemaTypeConfigProto SCHEMA_PROTO_1 = - SchemaTypeConfigProto.newBuilder().setSchemaType(SCHEMA_TYPE_1).build(); - private static final SchemaTypeConfigProto SCHEMA_PROTO_2 = - SchemaTypeConfigProto.newBuilder().setSchemaType(SCHEMA_TYPE_2).build(); - private static final String PREFIX = "package$databaseName/"; - private static final Map SCHEMA_MAP = - ImmutableMap.of( - PREFIX + SCHEMA_TYPE_1, SCHEMA_PROTO_1, PREFIX + SCHEMA_TYPE_2, SCHEMA_PROTO_2); - - @Test - public void testDocumentProtoConvert() { - GenericDocument document = - new GenericDocument.Builder>( - "namespace", "id1", SCHEMA_TYPE_1) - .setCreationTimestampMillis(5L) - .setScore(1) - .setTtlMillis(1L) - .setPropertyLong("longKey1", 1L) - .setPropertyDouble("doubleKey1", 1.0) - .setPropertyBoolean("booleanKey1", true) - .setPropertyString("stringKey1", "test-value1") - .setPropertyBytes("byteKey1", BYTE_ARRAY_1, BYTE_ARRAY_2) - .setPropertyDocument("documentKey1", DOCUMENT_PROPERTIES_1) - .setPropertyDocument("documentKey2", DOCUMENT_PROPERTIES_2) - .build(); - - // Create the Document proto. Need to sort the property order by key. - DocumentProto.Builder documentProtoBuilder = - DocumentProto.newBuilder() - .setUri("id1") - .setSchema(SCHEMA_TYPE_1) - .setCreationTimestampMs(5L) - .setScore(1) - .setTtlMs(1L) - .setNamespace("namespace"); - HashMap propertyProtoMap = new HashMap<>(); - propertyProtoMap.put( - "longKey1", PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L)); - propertyProtoMap.put( - "doubleKey1", - PropertyProto.newBuilder().setName("doubleKey1").addDoubleValues(1.0)); - propertyProtoMap.put( - "booleanKey1", - PropertyProto.newBuilder().setName("booleanKey1").addBooleanValues(true)); - propertyProtoMap.put( - "stringKey1", - PropertyProto.newBuilder().setName("stringKey1").addStringValues("test-value1")); - propertyProtoMap.put( - "byteKey1", - PropertyProto.newBuilder() - .setName("byteKey1") - .addBytesValues(ByteString.copyFrom(BYTE_ARRAY_1)) - .addBytesValues(ByteString.copyFrom(BYTE_ARRAY_2))); - propertyProtoMap.put( - "documentKey1", - PropertyProto.newBuilder() - .setName("documentKey1") - .addDocumentValues( - GenericDocumentToProtoConverter.toDocumentProto( - DOCUMENT_PROPERTIES_1))); - propertyProtoMap.put( - "documentKey2", - PropertyProto.newBuilder() - .setName("documentKey2") - .addDocumentValues( - GenericDocumentToProtoConverter.toDocumentProto( - DOCUMENT_PROPERTIES_2))); - List sortedKey = new ArrayList<>(propertyProtoMap.keySet()); - Collections.sort(sortedKey); - for (String key : sortedKey) { - documentProtoBuilder.addProperties(propertyProtoMap.get(key)); - } - DocumentProto documentProto = documentProtoBuilder.build(); - - GenericDocument convertedGenericDocument = - GenericDocumentToProtoConverter.toGenericDocument( - documentProto, PREFIX, SCHEMA_MAP); - DocumentProto convertedDocumentProto = - GenericDocumentToProtoConverter.toDocumentProto(document); - - assertThat(convertedDocumentProto).isEqualTo(documentProto); - assertThat(convertedGenericDocument).isEqualTo(document); - } - - @Test - public void testConvertDocument_whenPropertyHasEmptyList() { - String emptyStringPropertyName = "emptyStringProperty"; - DocumentProto documentProto = - DocumentProto.newBuilder() - .setUri("id1") - .setSchema(SCHEMA_TYPE_1) - .setCreationTimestampMs(5L) - .setNamespace("namespace") - .addProperties( - PropertyProto.newBuilder().setName(emptyStringPropertyName).build()) - .build(); - - PropertyConfigProto emptyStringListProperty = - PropertyConfigProto.newBuilder() - .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED) - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setPropertyName(emptyStringPropertyName) - .build(); - SchemaTypeConfigProto schemaTypeConfigProto = - SchemaTypeConfigProto.newBuilder() - .addProperties(emptyStringListProperty) - .setSchemaType(SCHEMA_TYPE_1) - .build(); - Map schemaMap = - ImmutableMap.of(PREFIX + SCHEMA_TYPE_1, schemaTypeConfigProto); - - GenericDocument convertedDocument = - GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX, schemaMap); - - GenericDocument expectedDocument = - new GenericDocument.Builder>( - "namespace", "id1", SCHEMA_TYPE_1) - .setCreationTimestampMillis(5L) - .setPropertyString(emptyStringPropertyName) - .build(); - assertThat(convertedDocument).isEqualTo(expectedDocument); - assertThat(expectedDocument.getPropertyStringArray(emptyStringPropertyName)).isEmpty(); - } - - @Test - public void testConvertDocument_whenNestedDocumentPropertyHasEmptyList() { - String emptyStringPropertyName = "emptyStringProperty"; - String documentPropertyName = "documentProperty"; - DocumentProto nestedDocumentProto = - DocumentProto.newBuilder() - .setUri("id2") - .setSchema(SCHEMA_TYPE_2) - .setCreationTimestampMs(5L) - .setNamespace("namespace") - .addProperties( - PropertyProto.newBuilder().setName(emptyStringPropertyName).build()) - .build(); - DocumentProto documentProto = - DocumentProto.newBuilder() - .setUri("id1") - .setSchema(SCHEMA_TYPE_1) - .setCreationTimestampMs(5L) - .setNamespace("namespace") - .addProperties( - PropertyProto.newBuilder() - .addDocumentValues(nestedDocumentProto) - .setName(documentPropertyName) - .build()) - .build(); - - PropertyConfigProto documentProperty = - PropertyConfigProto.newBuilder() - .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED) - .setDataType(PropertyConfigProto.DataType.Code.DOCUMENT) - .setPropertyName(documentPropertyName) - .setSchemaType(SCHEMA_TYPE_2) - .build(); - SchemaTypeConfigProto schemaTypeConfigProto = - SchemaTypeConfigProto.newBuilder() - .addProperties(documentProperty) - .setSchemaType(SCHEMA_TYPE_1) - .build(); - PropertyConfigProto emptyStringListProperty = - PropertyConfigProto.newBuilder() - .setCardinality(PropertyConfigProto.Cardinality.Code.REPEATED) - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setPropertyName(emptyStringPropertyName) - .build(); - SchemaTypeConfigProto nestedSchemaTypeConfigProto = - SchemaTypeConfigProto.newBuilder() - .addProperties(emptyStringListProperty) - .setSchemaType(SCHEMA_TYPE_2) - .build(); - Map schemaMap = - ImmutableMap.of( - PREFIX + SCHEMA_TYPE_1, - schemaTypeConfigProto, - PREFIX + SCHEMA_TYPE_2, - nestedSchemaTypeConfigProto); - - GenericDocument convertedDocument = - GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX, schemaMap); - - GenericDocument expectedDocument = - new GenericDocument.Builder>( - "namespace", "id1", SCHEMA_TYPE_1) - .setCreationTimestampMillis(5L) - .setPropertyDocument( - documentPropertyName, - new GenericDocument.Builder>( - "namespace", "id2", SCHEMA_TYPE_2) - .setCreationTimestampMillis(5L) - .setPropertyString(emptyStringPropertyName) - .build()) - .build(); - assertThat(convertedDocument).isEqualTo(expectedDocument); - assertThat( - expectedDocument - .getPropertyDocument(documentPropertyName) - .getPropertyStringArray(emptyStringPropertyName)) - .isEmpty(); - } -} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java deleted file mode 100644 index ebceba4ffa71a..0000000000000 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SchemaToProtoConverterTest.java +++ /dev/null @@ -1,151 +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 com.android.server.appsearch.external.localstorage.converter; - -import static com.google.common.truth.Truth.assertThat; - -import android.app.appsearch.AppSearchSchema; - -import com.android.server.appsearch.icing.proto.PropertyConfigProto; -import com.android.server.appsearch.icing.proto.SchemaTypeConfigProto; -import com.android.server.appsearch.icing.proto.StringIndexingConfig; -import com.android.server.appsearch.icing.proto.TermMatchType; - -import org.junit.Test; - -public class SchemaToProtoConverterTest { - @Test - public void testGetProto_Email() { - AppSearchSchema emailSchema = - new AppSearchSchema.Builder("Email") - .addProperty( - new AppSearchSchema.StringPropertyConfig.Builder("subject") - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .setIndexingType( - AppSearchSchema.StringPropertyConfig - .INDEXING_TYPE_PREFIXES) - .setTokenizerType( - AppSearchSchema.StringPropertyConfig - .TOKENIZER_TYPE_PLAIN) - .build()) - .addProperty( - new AppSearchSchema.StringPropertyConfig.Builder("body") - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .setIndexingType( - AppSearchSchema.StringPropertyConfig - .INDEXING_TYPE_PREFIXES) - .setTokenizerType( - AppSearchSchema.StringPropertyConfig - .TOKENIZER_TYPE_PLAIN) - .build()) - .build(); - - SchemaTypeConfigProto expectedEmailProto = - SchemaTypeConfigProto.newBuilder() - .setSchemaType("Email") - .setVersion(12345) - .addProperties( - PropertyConfigProto.newBuilder() - .setPropertyName("subject") - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setCardinality( - PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setStringIndexingConfig( - StringIndexingConfig.newBuilder() - .setTokenizerType( - StringIndexingConfig.TokenizerType - .Code.PLAIN) - .setTermMatchType( - TermMatchType.Code.PREFIX))) - .addProperties( - PropertyConfigProto.newBuilder() - .setPropertyName("body") - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setCardinality( - PropertyConfigProto.Cardinality.Code.OPTIONAL) - .setStringIndexingConfig( - StringIndexingConfig.newBuilder() - .setTokenizerType( - StringIndexingConfig.TokenizerType - .Code.PLAIN) - .setTermMatchType( - TermMatchType.Code.PREFIX))) - .build(); - - assertThat(SchemaToProtoConverter.toSchemaTypeConfigProto(emailSchema, /*version=*/ 12345)) - .isEqualTo(expectedEmailProto); - assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedEmailProto)) - .isEqualTo(emailSchema); - } - - @Test - public void testGetProto_MusicRecording() { - AppSearchSchema musicRecordingSchema = - new AppSearchSchema.Builder("MusicRecording") - .addProperty( - new AppSearchSchema.StringPropertyConfig.Builder("artist") - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) - .setIndexingType( - AppSearchSchema.StringPropertyConfig - .INDEXING_TYPE_PREFIXES) - .setTokenizerType( - AppSearchSchema.StringPropertyConfig - .TOKENIZER_TYPE_PLAIN) - .build()) - .addProperty( - new AppSearchSchema.LongPropertyConfig.Builder("pubDate") - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .build()) - .build(); - - SchemaTypeConfigProto expectedMusicRecordingProto = - SchemaTypeConfigProto.newBuilder() - .setSchemaType("MusicRecording") - .setVersion(0) - .addProperties( - PropertyConfigProto.newBuilder() - .setPropertyName("artist") - .setDataType(PropertyConfigProto.DataType.Code.STRING) - .setCardinality( - PropertyConfigProto.Cardinality.Code.REPEATED) - .setStringIndexingConfig( - StringIndexingConfig.newBuilder() - .setTokenizerType( - StringIndexingConfig.TokenizerType - .Code.PLAIN) - .setTermMatchType( - TermMatchType.Code.PREFIX))) - .addProperties( - PropertyConfigProto.newBuilder() - .setPropertyName("pubDate") - .setDataType(PropertyConfigProto.DataType.Code.INT64) - .setCardinality( - PropertyConfigProto.Cardinality.Code.OPTIONAL)) - .build(); - - assertThat( - SchemaToProtoConverter.toSchemaTypeConfigProto( - musicRecordingSchema, /*version=*/ 0)) - .isEqualTo(expectedMusicRecordingProto); - assertThat(SchemaToProtoConverter.toAppSearchSchema(expectedMusicRecordingProto)) - .isEqualTo(musicRecordingSchema); - } -} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java deleted file mode 100644 index 992961c0c23a2..0000000000000 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/converter/SnippetTest.java +++ /dev/null @@ -1,322 +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 com.android.server.appsearch.external.localstorage.converter; - -import static com.google.common.truth.Truth.assertThat; - -import android.app.appsearch.SearchResult; -import android.app.appsearch.SearchResultPage; - -import com.android.server.appsearch.external.localstorage.util.PrefixUtil; -import com.android.server.appsearch.icing.proto.DocumentProto; -import com.android.server.appsearch.icing.proto.PropertyProto; -import com.android.server.appsearch.icing.proto.SchemaTypeConfigProto; -import com.android.server.appsearch.icing.proto.SearchResultProto; -import com.android.server.appsearch.icing.proto.SnippetMatchProto; -import com.android.server.appsearch.icing.proto.SnippetProto; - -import org.junit.Test; - -import java.util.Collections; -import java.util.Map; - -public class SnippetTest { - private static final String SCHEMA_TYPE = "schema1"; - private static final String PACKAGE_NAME = "packageName"; - private static final String DATABASE_NAME = "databaseName"; - private static final String PREFIX = PrefixUtil.createPrefix(PACKAGE_NAME, DATABASE_NAME); - private static final SchemaTypeConfigProto SCHEMA_TYPE_CONFIG_PROTO = - SchemaTypeConfigProto.newBuilder().setSchemaType(PREFIX + SCHEMA_TYPE).build(); - private static final Map> SCHEMA_MAP = - Collections.singletonMap( - PREFIX, - Collections.singletonMap(PREFIX + SCHEMA_TYPE, SCHEMA_TYPE_CONFIG_PROTO)); - - @Test - public void testSingleStringSnippet() { - final String propertyKeyString = "content"; - final String propertyValueString = - "A commonly used fake word is foo.\n" - + " Another nonsense word that’s used a lot\n" - + " is bar.\n"; - final String id = "id1"; - final String exactMatch = "foo"; - final String window = "is foo"; - - // Building the SearchResult received from query. - DocumentProto documentProto = - DocumentProto.newBuilder() - .setUri(id) - .setSchema(SCHEMA_TYPE) - .addProperties( - PropertyProto.newBuilder() - .setName(propertyKeyString) - .addStringValues(propertyValueString)) - .build(); - SnippetProto snippetProto = - SnippetProto.newBuilder() - .addEntries( - SnippetProto.EntryProto.newBuilder() - .setPropertyName(propertyKeyString) - .addSnippetMatches( - SnippetMatchProto.newBuilder() - .setExactMatchBytePosition(29) - .setExactMatchByteLength(3) - .setExactMatchUtf16Position(29) - .setExactMatchUtf16Length(3) - .setWindowBytePosition(26) - .setWindowByteLength(6) - .setWindowUtf16Position(26) - .setWindowUtf16Length(6))) - .build(); - SearchResultProto searchResultProto = - SearchResultProto.newBuilder() - .addResults( - SearchResultProto.ResultProto.newBuilder() - .setDocument(documentProto) - .setSnippet(snippetProto)) - .build(); - - // Making ResultReader and getting Snippet values. - SearchResultPage searchResultPage = - SearchResultToProtoConverter.toSearchResultPage( - searchResultProto, - Collections.singletonList(PACKAGE_NAME), - Collections.singletonList(DATABASE_NAME), - SCHEMA_MAP); - assertThat(searchResultPage.getResults()).hasSize(1); - SearchResult.MatchInfo match = searchResultPage.getResults().get(0).getMatchInfos().get(0); - assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString); - assertThat(match.getFullText()).isEqualTo(propertyValueString); - assertThat(match.getExactMatch()).isEqualTo(exactMatch); - assertThat(match.getExactMatchRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 29, /*upper=*/ 32)); - assertThat(match.getFullText()).isEqualTo(propertyValueString); - assertThat(match.getSnippetRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 26, /*upper=*/ 32)); - assertThat(match.getSnippet()).isEqualTo(window); - } - - @Test - public void testNoSnippets() { - final String propertyKeyString = "content"; - final String propertyValueString = - "A commonly used fake word is foo.\n" - + " Another nonsense word that’s used a lot\n" - + " is bar.\n"; - final String id = "id1"; - - // Building the SearchResult received from query. - DocumentProto documentProto = - DocumentProto.newBuilder() - .setUri(id) - .setSchema(SCHEMA_TYPE) - .addProperties( - PropertyProto.newBuilder() - .setName(propertyKeyString) - .addStringValues(propertyValueString)) - .build(); - SearchResultProto searchResultProto = - SearchResultProto.newBuilder() - .addResults( - SearchResultProto.ResultProto.newBuilder() - .setDocument(documentProto)) - .build(); - - SearchResultPage searchResultPage = - SearchResultToProtoConverter.toSearchResultPage( - searchResultProto, - Collections.singletonList(PACKAGE_NAME), - Collections.singletonList(DATABASE_NAME), - SCHEMA_MAP); - assertThat(searchResultPage.getResults()).hasSize(1); - assertThat(searchResultPage.getResults().get(0).getMatchInfos()).isEmpty(); - } - - @Test - public void testMultipleStringSnippet() { - // Building the SearchResult received from query. - DocumentProto documentProto = - DocumentProto.newBuilder() - .setUri("uri1") - .setSchema(SCHEMA_TYPE) - .addProperties( - PropertyProto.newBuilder() - .setName("senderName") - .addStringValues("Test Name Jr.")) - .addProperties( - PropertyProto.newBuilder() - .setName("senderEmail") - .addStringValues("TestNameJr@gmail.com")) - .build(); - SnippetProto snippetProto = - SnippetProto.newBuilder() - .addEntries( - SnippetProto.EntryProto.newBuilder() - .setPropertyName("senderName") - .addSnippetMatches( - SnippetMatchProto.newBuilder() - .setExactMatchBytePosition(0) - .setExactMatchByteLength(4) - .setExactMatchUtf16Position(0) - .setExactMatchUtf16Length(4) - .setWindowBytePosition(0) - .setWindowByteLength(9) - .setWindowUtf16Position(0) - .setWindowUtf16Length(9))) - .addEntries( - SnippetProto.EntryProto.newBuilder() - .setPropertyName("senderEmail") - .addSnippetMatches( - SnippetMatchProto.newBuilder() - .setExactMatchBytePosition(0) - .setExactMatchByteLength(20) - .setExactMatchUtf16Position(0) - .setExactMatchUtf16Length(20) - .setWindowBytePosition(0) - .setWindowByteLength(20) - .setWindowUtf16Position(0) - .setWindowUtf16Length(20))) - .build(); - SearchResultProto searchResultProto = - SearchResultProto.newBuilder() - .addResults( - SearchResultProto.ResultProto.newBuilder() - .setDocument(documentProto) - .setSnippet(snippetProto)) - .build(); - - // Making ResultReader and getting Snippet values. - SearchResultPage searchResultPage = - SearchResultToProtoConverter.toSearchResultPage( - searchResultProto, - Collections.singletonList(PACKAGE_NAME), - Collections.singletonList(DATABASE_NAME), - SCHEMA_MAP); - assertThat(searchResultPage.getResults()).hasSize(1); - SearchResult.MatchInfo match1 = searchResultPage.getResults().get(0).getMatchInfos().get(0); - assertThat(match1.getPropertyPath()).isEqualTo("senderName"); - assertThat(match1.getFullText()).isEqualTo("Test Name Jr."); - assertThat(match1.getExactMatchRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 4)); - assertThat(match1.getExactMatch()).isEqualTo("Test"); - assertThat(match1.getSnippetRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9)); - assertThat(match1.getSnippet()).isEqualTo("Test Name"); - - SearchResult.MatchInfo match2 = searchResultPage.getResults().get(0).getMatchInfos().get(1); - assertThat(match2.getPropertyPath()).isEqualTo("senderEmail"); - assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com"); - assertThat(match2.getExactMatchRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20)); - assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com"); - assertThat(match2.getSnippetRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 20)); - assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com"); - } - - @Test - public void testNestedDocumentSnippet() { - // Building the SearchResult received from query. - DocumentProto documentProto = - DocumentProto.newBuilder() - .setUri("id1") - .setSchema(SCHEMA_TYPE) - .addProperties( - PropertyProto.newBuilder() - .setName("sender") - .addDocumentValues( - DocumentProto.newBuilder() - .addProperties( - PropertyProto.newBuilder() - .setName("name") - .addStringValues( - "Test Name Jr.")) - .addProperties( - PropertyProto.newBuilder() - .setName("email") - .addStringValues( - "TestNameJr@gmail.com") - .addStringValues( - "TestNameJr2@gmail.com")))) - .build(); - SnippetProto snippetProto = - SnippetProto.newBuilder() - .addEntries( - SnippetProto.EntryProto.newBuilder() - .setPropertyName("sender.name") - .addSnippetMatches( - SnippetMatchProto.newBuilder() - .setExactMatchBytePosition(0) - .setExactMatchByteLength(4) - .setExactMatchUtf16Position(0) - .setExactMatchUtf16Length(4) - .setWindowBytePosition(0) - .setWindowByteLength(9) - .setWindowUtf16Position(0) - .setWindowUtf16Length(9))) - .addEntries( - SnippetProto.EntryProto.newBuilder() - .setPropertyName("sender.email[1]") - .addSnippetMatches( - SnippetMatchProto.newBuilder() - .setExactMatchBytePosition(0) - .setExactMatchByteLength(21) - .setExactMatchUtf16Position(0) - .setExactMatchUtf16Length(21) - .setWindowBytePosition(0) - .setWindowByteLength(21) - .setWindowUtf16Position(0) - .setWindowUtf16Length(21))) - .build(); - SearchResultProto searchResultProto = - SearchResultProto.newBuilder() - .addResults( - SearchResultProto.ResultProto.newBuilder() - .setDocument(documentProto) - .setSnippet(snippetProto)) - .build(); - - // Making ResultReader and getting Snippet values. - SearchResultPage searchResultPage = - SearchResultToProtoConverter.toSearchResultPage( - searchResultProto, - Collections.singletonList(PACKAGE_NAME), - Collections.singletonList(DATABASE_NAME), - SCHEMA_MAP); - assertThat(searchResultPage.getResults()).hasSize(1); - SearchResult.MatchInfo match1 = searchResultPage.getResults().get(0).getMatchInfos().get(0); - assertThat(match1.getPropertyPath()).isEqualTo("sender.name"); - assertThat(match1.getFullText()).isEqualTo("Test Name Jr."); - assertThat(match1.getExactMatchRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 4)); - assertThat(match1.getExactMatch()).isEqualTo("Test"); - assertThat(match1.getSnippetRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 9)); - assertThat(match1.getSnippet()).isEqualTo("Test Name"); - - SearchResult.MatchInfo match2 = searchResultPage.getResults().get(0).getMatchInfos().get(1); - assertThat(match2.getPropertyPath()).isEqualTo("sender.email[1]"); - assertThat(match2.getFullText()).isEqualTo("TestNameJr2@gmail.com"); - assertThat(match2.getExactMatchRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 21)); - assertThat(match2.getExactMatch()).isEqualTo("TestNameJr2@gmail.com"); - assertThat(match2.getSnippetRange()) - .isEqualTo(new SearchResult.MatchRange(/*lower=*/ 0, /*upper=*/ 21)); - assertThat(match2.getSnippet()).isEqualTo("TestNameJr2@gmail.com"); - } -} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java deleted file mode 100644 index c1dc0e447c70d..0000000000000 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java +++ /dev/null @@ -1,396 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.appsearch.external.localstorage.stats; - -import static com.google.common.truth.Truth.assertThat; - -import android.app.appsearch.AppSearchResult; - -import org.junit.Test; - -public class AppSearchStatsTest { - static final String TEST_PACKAGE_NAME = "com.google.test"; - static final String TEST_DATA_BASE = "testDataBase"; - static final int TEST_STATUS_CODE = AppSearchResult.RESULT_INTERNAL_ERROR; - static final int TEST_TOTAL_LATENCY_MILLIS = 20; - - @Test - public void testAppSearchStats_CallStats() { - final int estimatedBinderLatencyMillis = 1; - final int numOperationsSucceeded = 2; - final int numOperationsFailed = 3; - final @CallStats.CallType int callType = CallStats.CALL_TYPE_PUT_DOCUMENTS; - - final CallStats cStats = - new CallStats.Builder() - .setPackageName(TEST_PACKAGE_NAME) - .setDatabase(TEST_DATA_BASE) - .setStatusCode(TEST_STATUS_CODE) - .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) - .setCallType(callType) - .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) - .setNumOperationsSucceeded(numOperationsSucceeded) - .setNumOperationsFailed(numOperationsFailed) - .build(); - - assertThat(cStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME); - assertThat(cStats.getDatabase()).isEqualTo(TEST_DATA_BASE); - assertThat(cStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE); - assertThat(cStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); - assertThat(cStats.getEstimatedBinderLatencyMillis()) - .isEqualTo(estimatedBinderLatencyMillis); - assertThat(cStats.getCallType()).isEqualTo(callType); - assertThat(cStats.getNumOperationsSucceeded()).isEqualTo(numOperationsSucceeded); - assertThat(cStats.getNumOperationsFailed()).isEqualTo(numOperationsFailed); - } - - @Test - public void testAppSearchCallStats_nullValues() { - final @CallStats.CallType int callType = CallStats.CALL_TYPE_PUT_DOCUMENTS; - - final CallStats.Builder cStatsBuilder = new CallStats.Builder().setCallType(callType); - - final CallStats cStats = cStatsBuilder.build(); - - assertThat(cStats.getPackageName()).isNull(); - assertThat(cStats.getDatabase()).isNull(); - assertThat(cStats.getCallType()).isEqualTo(callType); - } - - @Test - public void testAppSearchStats_PutDocumentStats() { - final int generateDocumentProtoLatencyMillis = 1; - final int rewriteDocumentTypesLatencyMillis = 2; - final int nativeLatencyMillis = 3; - final int nativeDocumentStoreLatencyMillis = 4; - final int nativeIndexLatencyMillis = 5; - final int nativeIndexMergeLatencyMillis = 6; - final int nativeDocumentSize = 7; - final int nativeNumTokensIndexed = 8; - final boolean nativeExceededMaxNumTokens = true; - final PutDocumentStats.Builder pStatsBuilder = - new PutDocumentStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) - .setStatusCode(TEST_STATUS_CODE) - .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) - .setGenerateDocumentProtoLatencyMillis(generateDocumentProtoLatencyMillis) - .setRewriteDocumentTypesLatencyMillis(rewriteDocumentTypesLatencyMillis) - .setNativeLatencyMillis(nativeLatencyMillis) - .setNativeDocumentStoreLatencyMillis(nativeDocumentStoreLatencyMillis) - .setNativeIndexLatencyMillis(nativeIndexLatencyMillis) - .setNativeIndexMergeLatencyMillis(nativeIndexMergeLatencyMillis) - .setNativeDocumentSizeBytes(nativeDocumentSize) - .setNativeNumTokensIndexed(nativeNumTokensIndexed) - .setNativeExceededMaxNumTokens(nativeExceededMaxNumTokens); - - final PutDocumentStats pStats = pStatsBuilder.build(); - - assertThat(pStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME); - assertThat(pStats.getDatabase()).isEqualTo(TEST_DATA_BASE); - assertThat(pStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE); - assertThat(pStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); - assertThat(pStats.getGenerateDocumentProtoLatencyMillis()) - .isEqualTo(generateDocumentProtoLatencyMillis); - assertThat(pStats.getRewriteDocumentTypesLatencyMillis()) - .isEqualTo(rewriteDocumentTypesLatencyMillis); - assertThat(pStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); - assertThat(pStats.getNativeDocumentStoreLatencyMillis()) - .isEqualTo(nativeDocumentStoreLatencyMillis); - assertThat(pStats.getNativeIndexLatencyMillis()).isEqualTo(nativeIndexLatencyMillis); - assertThat(pStats.getNativeIndexMergeLatencyMillis()) - .isEqualTo(nativeIndexMergeLatencyMillis); - assertThat(pStats.getNativeDocumentSizeBytes()).isEqualTo(nativeDocumentSize); - assertThat(pStats.getNativeNumTokensIndexed()).isEqualTo(nativeNumTokensIndexed); - assertThat(pStats.getNativeExceededMaxNumTokens()).isEqualTo(nativeExceededMaxNumTokens); - } - - @Test - public void testAppSearchStats_InitializeStats() { - int prepareSchemaAndNamespacesLatencyMillis = 1; - int prepareVisibilityFileLatencyMillis = 2; - int nativeLatencyMillis = 3; - int nativeDocumentStoreRecoveryCause = 4; - int nativeIndexRestorationCause = 5; - int nativeSchemaStoreRecoveryCause = 6; - int nativeDocumentStoreRecoveryLatencyMillis = 7; - int nativeIndexRestorationLatencyMillis = 8; - int nativeSchemaStoreRecoveryLatencyMillis = 9; - int nativeDocumentStoreDataStatus = 10; - int nativeNumDocuments = 11; - int nativeNumSchemaTypes = 12; - - final InitializeStats.Builder iStatsBuilder = - new InitializeStats.Builder() - .setStatusCode(TEST_STATUS_CODE) - .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) - .setHasDeSync(/* hasDeSyncs= */ true) - .setPrepareSchemaAndNamespacesLatencyMillis( - prepareSchemaAndNamespacesLatencyMillis) - .setPrepareVisibilityStoreLatencyMillis(prepareVisibilityFileLatencyMillis) - .setNativeLatencyMillis(nativeLatencyMillis) - .setDocumentStoreRecoveryCause(nativeDocumentStoreRecoveryCause) - .setIndexRestorationCause(nativeIndexRestorationCause) - .setSchemaStoreRecoveryCause(nativeSchemaStoreRecoveryCause) - .setDocumentStoreRecoveryLatencyMillis( - nativeDocumentStoreRecoveryLatencyMillis) - .setIndexRestorationLatencyMillis(nativeIndexRestorationLatencyMillis) - .setSchemaStoreRecoveryLatencyMillis(nativeSchemaStoreRecoveryLatencyMillis) - .setDocumentStoreDataStatus(nativeDocumentStoreDataStatus) - .setDocumentCount(nativeNumDocuments) - .setSchemaTypeCount(nativeNumSchemaTypes) - .setHasReset(true) - .setResetStatusCode(AppSearchResult.RESULT_INVALID_SCHEMA); - final InitializeStats iStats = iStatsBuilder.build(); - - assertThat(iStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE); - assertThat(iStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); - assertThat(iStats.hasDeSync()).isTrue(); - assertThat(iStats.getPrepareSchemaAndNamespacesLatencyMillis()) - .isEqualTo(prepareSchemaAndNamespacesLatencyMillis); - assertThat(iStats.getPrepareVisibilityStoreLatencyMillis()) - .isEqualTo(prepareVisibilityFileLatencyMillis); - assertThat(iStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); - assertThat(iStats.getDocumentStoreRecoveryCause()) - .isEqualTo(nativeDocumentStoreRecoveryCause); - assertThat(iStats.getIndexRestorationCause()).isEqualTo(nativeIndexRestorationCause); - assertThat(iStats.getSchemaStoreRecoveryCause()).isEqualTo(nativeSchemaStoreRecoveryCause); - assertThat(iStats.getDocumentStoreRecoveryLatencyMillis()) - .isEqualTo(nativeDocumentStoreRecoveryLatencyMillis); - assertThat(iStats.getIndexRestorationLatencyMillis()) - .isEqualTo(nativeIndexRestorationLatencyMillis); - assertThat(iStats.getSchemaStoreRecoveryLatencyMillis()) - .isEqualTo(nativeSchemaStoreRecoveryLatencyMillis); - assertThat(iStats.getDocumentStoreDataStatus()).isEqualTo(nativeDocumentStoreDataStatus); - assertThat(iStats.getDocumentCount()).isEqualTo(nativeNumDocuments); - assertThat(iStats.getSchemaTypeCount()).isEqualTo(nativeNumSchemaTypes); - assertThat(iStats.hasReset()).isTrue(); - assertThat(iStats.getResetStatusCode()).isEqualTo(AppSearchResult.RESULT_INVALID_SCHEMA); - } - - @Test - public void testAppSearchStats_SearchStats() { - int rewriteSearchSpecLatencyMillis = 1; - int rewriteSearchResultLatencyMillis = 2; - int visibilityScope = SearchStats.VISIBILITY_SCOPE_LOCAL; - int nativeLatencyMillis = 4; - int nativeNumTerms = 5; - int nativeQueryLength = 6; - int nativeNumNamespacesFiltered = 7; - int nativeNumSchemaTypesFiltered = 8; - int nativeRequestedPageSize = 9; - int nativeNumResultsReturnedCurrentPage = 10; - boolean nativeIsFirstPage = true; - int nativeParseQueryLatencyMillis = 11; - int nativeRankingStrategy = 12; - int nativeNumDocumentsScored = 13; - int nativeScoringLatencyMillis = 14; - int nativeRankingLatencyMillis = 15; - int nativeNumResultsSnippeted = 16; - int nativeDocumentRetrievingLatencyMillis = 17; - final SearchStats.Builder sStatsBuilder = - new SearchStats.Builder(visibilityScope, TEST_PACKAGE_NAME) - .setDatabase(TEST_DATA_BASE) - .setStatusCode(TEST_STATUS_CODE) - .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) - .setRewriteSearchSpecLatencyMillis(rewriteSearchSpecLatencyMillis) - .setRewriteSearchResultLatencyMillis(rewriteSearchResultLatencyMillis) - .setNativeLatencyMillis(nativeLatencyMillis) - .setTermCount(nativeNumTerms) - .setQueryLength(nativeQueryLength) - .setFilteredNamespaceCount(nativeNumNamespacesFiltered) - .setFilteredSchemaTypeCount(nativeNumSchemaTypesFiltered) - .setRequestedPageSize(nativeRequestedPageSize) - .setCurrentPageReturnedResultCount(nativeNumResultsReturnedCurrentPage) - .setIsFirstPage(nativeIsFirstPage) - .setParseQueryLatencyMillis(nativeParseQueryLatencyMillis) - .setRankingStrategy(nativeRankingStrategy) - .setScoredDocumentCount(nativeNumDocumentsScored) - .setScoringLatencyMillis(nativeScoringLatencyMillis) - .setRankingLatencyMillis(nativeRankingLatencyMillis) - .setResultWithSnippetsCount(nativeNumResultsSnippeted) - .setDocumentRetrievingLatencyMillis(nativeDocumentRetrievingLatencyMillis); - final SearchStats sStats = sStatsBuilder.build(); - - assertThat(sStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME); - assertThat(sStats.getDatabase()).isEqualTo(TEST_DATA_BASE); - assertThat(sStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE); - assertThat(sStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); - assertThat(sStats.getRewriteSearchSpecLatencyMillis()) - .isEqualTo(rewriteSearchSpecLatencyMillis); - assertThat(sStats.getRewriteSearchResultLatencyMillis()) - .isEqualTo(rewriteSearchResultLatencyMillis); - assertThat(sStats.getVisibilityScope()).isEqualTo(visibilityScope); - assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); - assertThat(sStats.getTermCount()).isEqualTo(nativeNumTerms); - assertThat(sStats.getQueryLength()).isEqualTo(nativeQueryLength); - assertThat(sStats.getFilteredNamespaceCount()).isEqualTo(nativeNumNamespacesFiltered); - assertThat(sStats.getFilteredSchemaTypeCount()).isEqualTo(nativeNumSchemaTypesFiltered); - assertThat(sStats.getRequestedPageSize()).isEqualTo(nativeRequestedPageSize); - assertThat(sStats.getCurrentPageReturnedResultCount()) - .isEqualTo(nativeNumResultsReturnedCurrentPage); - assertThat(sStats.isFirstPage()).isTrue(); - assertThat(sStats.getParseQueryLatencyMillis()).isEqualTo(nativeParseQueryLatencyMillis); - assertThat(sStats.getRankingStrategy()).isEqualTo(nativeRankingStrategy); - assertThat(sStats.getScoredDocumentCount()).isEqualTo(nativeNumDocumentsScored); - assertThat(sStats.getScoringLatencyMillis()).isEqualTo(nativeScoringLatencyMillis); - assertThat(sStats.getRankingLatencyMillis()).isEqualTo(nativeRankingLatencyMillis); - assertThat(sStats.getResultWithSnippetsCount()).isEqualTo(nativeNumResultsSnippeted); - assertThat(sStats.getDocumentRetrievingLatencyMillis()) - .isEqualTo(nativeDocumentRetrievingLatencyMillis); - } - - @Test - public void testAppSearchStats_SetSchemaStats() { - SchemaMigrationStats schemaMigrationStats = - new SchemaMigrationStats.Builder() - .setGetSchemaLatencyMillis(1) - .setQueryAndTransformLatencyMillis(2) - .setFirstSetSchemaLatencyMillis(3) - .setSecondSetSchemaLatencyMillis(4) - .setSaveDocumentLatencyMillis(5) - .setMigratedDocumentCount(6) - .setSavedDocumentCount(7) - .build(); - int nativeLatencyMillis = 1; - int newTypeCount = 2; - int compatibleTypeChangeCount = 3; - int indexIncompatibleTypeChangeCount = 4; - int backwardsIncompatibleTypeChangeCount = 5; - SetSchemaStats sStats = - new SetSchemaStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) - .setStatusCode(TEST_STATUS_CODE) - .setSchemaMigrationStats(schemaMigrationStats) - .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) - .setNativeLatencyMillis(nativeLatencyMillis) - .setNewTypeCount(newTypeCount) - .setCompatibleTypeChangeCount(compatibleTypeChangeCount) - .setIndexIncompatibleTypeChangeCount(indexIncompatibleTypeChangeCount) - .setBackwardsIncompatibleTypeChangeCount( - backwardsIncompatibleTypeChangeCount) - .build(); - - assertThat(sStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME); - assertThat(sStats.getDatabase()).isEqualTo(TEST_DATA_BASE); - assertThat(sStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE); - assertThat(sStats.getSchemaMigrationStats()).isEqualTo(schemaMigrationStats); - assertThat(sStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); - assertThat(sStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); - assertThat(sStats.getNewTypeCount()).isEqualTo(newTypeCount); - assertThat(sStats.getCompatibleTypeChangeCount()).isEqualTo(compatibleTypeChangeCount); - assertThat(sStats.getIndexIncompatibleTypeChangeCount()) - .isEqualTo(indexIncompatibleTypeChangeCount); - assertThat(sStats.getBackwardsIncompatibleTypeChangeCount()) - .isEqualTo(backwardsIncompatibleTypeChangeCount); - } - - @Test - public void testAppSearchStats_SchemaMigrationStats() { - int getSchemaLatency = 1; - int queryAndTransformLatency = 2; - int firstSetSchemaLatency = 3; - int secondSetSchemaLatency = 4; - int saveDocumentLatency = 5; - int migratedDocumentCount = 6; - int savedDocumentCount = 7; - SchemaMigrationStats sStats = - new SchemaMigrationStats.Builder() - .setGetSchemaLatencyMillis(getSchemaLatency) - .setQueryAndTransformLatencyMillis(queryAndTransformLatency) - .setFirstSetSchemaLatencyMillis(firstSetSchemaLatency) - .setSecondSetSchemaLatencyMillis(secondSetSchemaLatency) - .setSaveDocumentLatencyMillis(saveDocumentLatency) - .setMigratedDocumentCount(migratedDocumentCount) - .setSavedDocumentCount(savedDocumentCount) - .build(); - - assertThat(sStats.getGetSchemaLatencyMillis()).isEqualTo(getSchemaLatency); - assertThat(sStats.getQueryAndTransformLatencyMillis()).isEqualTo(queryAndTransformLatency); - assertThat(sStats.getFirstSetSchemaLatencyMillis()).isEqualTo(firstSetSchemaLatency); - assertThat(sStats.getSecondSetSchemaLatencyMillis()).isEqualTo(secondSetSchemaLatency); - assertThat(sStats.getSaveDocumentLatencyMillis()).isEqualTo(saveDocumentLatency); - assertThat(sStats.getMigratedDocumentCount()).isEqualTo(migratedDocumentCount); - assertThat(sStats.getSavedDocumentCount()).isEqualTo(savedDocumentCount); - } - - @Test - public void testAppSearchStats_RemoveStats() { - int nativeLatencyMillis = 1; - @RemoveStats.DeleteType int deleteType = 2; - int documentDeletedCount = 3; - - final RemoveStats rStats = - new RemoveStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE) - .setStatusCode(TEST_STATUS_CODE) - .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) - .setNativeLatencyMillis(nativeLatencyMillis) - .setDeleteType(deleteType) - .setDeletedDocumentCount(documentDeletedCount) - .build(); - - assertThat(rStats.getPackageName()).isEqualTo(TEST_PACKAGE_NAME); - assertThat(rStats.getDatabase()).isEqualTo(TEST_DATA_BASE); - assertThat(rStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE); - assertThat(rStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); - assertThat(rStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); - assertThat(rStats.getDeleteType()).isEqualTo(deleteType); - assertThat(rStats.getDeletedDocumentCount()).isEqualTo(documentDeletedCount); - } - - @Test - public void testAppSearchStats_OptimizeStats() { - int nativeLatencyMillis = 1; - int nativeDocumentStoreOptimizeLatencyMillis = 2; - int nativeIndexRestorationLatencyMillis = 3; - int nativeNumOriginalDocuments = 4; - int nativeNumDeletedDocuments = 5; - int nativeNumExpiredDocuments = 6; - long nativeStorageSizeBeforeBytes = Integer.MAX_VALUE + 1; - long nativeStorageSizeAfterBytes = Integer.MAX_VALUE + 2; - long nativeTimeSinceLastOptimizeMillis = Integer.MAX_VALUE + 3; - - final OptimizeStats oStats = - new OptimizeStats.Builder() - .setStatusCode(TEST_STATUS_CODE) - .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS) - .setNativeLatencyMillis(nativeLatencyMillis) - .setDocumentStoreOptimizeLatencyMillis( - nativeDocumentStoreOptimizeLatencyMillis) - .setIndexRestorationLatencyMillis(nativeIndexRestorationLatencyMillis) - .setOriginalDocumentCount(nativeNumOriginalDocuments) - .setDeletedDocumentCount(nativeNumDeletedDocuments) - .setExpiredDocumentCount(nativeNumExpiredDocuments) - .setStorageSizeBeforeBytes(nativeStorageSizeBeforeBytes) - .setStorageSizeAfterBytes(nativeStorageSizeAfterBytes) - .setTimeSinceLastOptimizeMillis(nativeTimeSinceLastOptimizeMillis) - .build(); - - assertThat(oStats.getStatusCode()).isEqualTo(TEST_STATUS_CODE); - assertThat(oStats.getTotalLatencyMillis()).isEqualTo(TEST_TOTAL_LATENCY_MILLIS); - assertThat(oStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); - assertThat(oStats.getNativeLatencyMillis()).isEqualTo(nativeLatencyMillis); - assertThat(oStats.getDocumentStoreOptimizeLatencyMillis()) - .isEqualTo(nativeDocumentStoreOptimizeLatencyMillis); - assertThat(oStats.getIndexRestorationLatencyMillis()) - .isEqualTo(nativeIndexRestorationLatencyMillis); - assertThat(oStats.getOriginalDocumentCount()).isEqualTo(nativeNumOriginalDocuments); - assertThat(oStats.getDeletedDocumentCount()).isEqualTo(nativeNumDeletedDocuments); - assertThat(oStats.getExpiredDocumentCount()).isEqualTo(nativeNumExpiredDocuments); - assertThat(oStats.getStorageSizeBeforeBytes()).isEqualTo(nativeStorageSizeBeforeBytes); - assertThat(oStats.getStorageSizeAfterBytes()).isEqualTo(nativeStorageSizeAfterBytes); - assertThat(oStats.getTimeSinceLastOptimizeMillis()) - .isEqualTo(nativeTimeSinceLastOptimizeMillis); - } -} diff --git a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java deleted file mode 100644 index ec96d6a71be40..0000000000000 --- a/services/tests/servicestests/src/com/android/server/appsearch/stats/PlatformLoggerTest.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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.stats; - -import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.pm.PackageManager; -import android.os.UserHandle; -import android.util.ArrayMap; - -import androidx.test.core.app.ApplicationProvider; - -import com.android.server.appsearch.AppSearchConfig; -import com.android.server.appsearch.external.localstorage.stats.CallStats; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import java.io.UnsupportedEncodingException; -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Map; - -/** - * Tests covering the functionalities in {@link PlatformLogger} NOT requiring overriding any flags - * in {@link android.provider.DeviceConfig}. - * - *

    To add tests rely on overriding the flags, please add them in the - * tests for {@link PlatformLogger} in mockingservicestests. - */ -public class PlatformLoggerTest { - private final Map mMockPackageManagers = new ArrayMap<>(); - private Context mContext; - - @Before - public void setUp() throws Exception { - Context context = ApplicationProvider.getApplicationContext(); - mContext = new ContextWrapper(context) { - @Override - public PackageManager getPackageManager() { - return getMockPackageManager(mContext.getUser()); - } - }; - } - - @Test - public void testCalculateHashCode_MD5_int32_shortString() - throws NoSuchAlgorithmException, UnsupportedEncodingException { - final String str1 = "d1"; - final String str2 = "d2"; - - int hashCodeForStr1 = PlatformLogger.calculateHashCodeMd5(str1); - - // hashing should be stable - assertThat(hashCodeForStr1).isEqualTo( - PlatformLogger.calculateHashCodeMd5(str1)); - assertThat(hashCodeForStr1).isNotEqualTo( - PlatformLogger.calculateHashCodeMd5(str2)); - } - - @Test - public void testGetCalculateCode_MD5_int32_mediumString() - throws NoSuchAlgorithmException, UnsupportedEncodingException { - final String str1 = "Siblings"; - final String str2 = "Teheran"; - - int hashCodeForStr1 = PlatformLogger.calculateHashCodeMd5(str1); - - // hashing should be stable - assertThat(hashCodeForStr1).isEqualTo( - PlatformLogger.calculateHashCodeMd5(str1)); - assertThat(hashCodeForStr1).isNotEqualTo( - PlatformLogger.calculateHashCodeMd5(str2)); - } - - @Test - public void testCalculateHashCode_MD5_int32_longString() throws NoSuchAlgorithmException, - UnsupportedEncodingException { - final String str1 = "abcdefghijkl-mnopqrstuvwxyz"; - final String str2 = "abcdefghijkl-mnopqrstuvwxy123"; - - int hashCodeForStr1 = PlatformLogger.calculateHashCodeMd5(str1); - - // hashing should be stable - assertThat(hashCodeForStr1).isEqualTo( - PlatformLogger.calculateHashCodeMd5(str1)); - assertThat(hashCodeForStr1).isNotEqualTo( - PlatformLogger.calculateHashCodeMd5(str2)); - } - - @Test - public void testCalculateHashCode_MD5_int32_sameAsBigInteger_intValue() throws - NoSuchAlgorithmException, UnsupportedEncodingException { - final String emptyStr = ""; - final String shortStr = "a"; - final String mediumStr = "Teheran"; - final String longStr = "abcd-efgh-ijkl-mnop-qrst-uvwx-yz"; - - int emptyHashCode = PlatformLogger.calculateHashCodeMd5(emptyStr); - int shortHashCode = PlatformLogger.calculateHashCodeMd5(shortStr); - int mediumHashCode = PlatformLogger.calculateHashCodeMd5(mediumStr); - int longHashCode = PlatformLogger.calculateHashCodeMd5(longStr); - - assertThat(emptyHashCode).isEqualTo(calculateHashCodeMd5withBigInteger(emptyStr)); - assertThat(shortHashCode).isEqualTo(calculateHashCodeMd5withBigInteger(shortStr)); - assertThat(mediumHashCode).isEqualTo(calculateHashCodeMd5withBigInteger(mediumStr)); - assertThat(longHashCode).isEqualTo(calculateHashCodeMd5withBigInteger(longStr)); - } - - @Test - public void testCalculateHashCode_MD5_strIsNull() throws - NoSuchAlgorithmException, UnsupportedEncodingException { - assertThat(PlatformLogger.calculateHashCodeMd5(/*str=*/ null)).isEqualTo(-1); - } - - /** Makes sure the caching works while getting the UID for calling package. */ - @Test - public void testGetPackageUidAsUser() throws Exception { - final String testPackageName = "packageName"; - final int testUid = 1234; - PlatformLogger logger = new PlatformLogger( - mContext, - AppSearchConfig.create(DIRECT_EXECUTOR)); - PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); - when(mockPackageManager.getPackageUid(testPackageName, /*flags=*/0)).thenReturn(testUid); - - // First time, no cache - PlatformLogger.ExtraStats extraStats = logger.createExtraStatsLocked(testPackageName, - CallStats.CALL_TYPE_PUT_DOCUMENT); - verify(mockPackageManager, times(1)) - .getPackageUid(eq(testPackageName), /*flags=*/ anyInt()); - assertThat(extraStats.mPackageUid).isEqualTo(testUid); - - // Second time, we have cache - extraStats = logger.createExtraStatsLocked(testPackageName, - CallStats.CALL_TYPE_PUT_DOCUMENT); - - // Count is still one since we will use the cache - verify(mockPackageManager, times(1)) - .getPackageUid(eq(testPackageName), /*flags=*/ anyInt()); - assertThat(extraStats.mPackageUid).isEqualTo(testUid); - - // Remove the cache and try again - assertThat(logger.removeCachedUidForPackage(testPackageName)).isEqualTo(testUid); - extraStats = logger.createExtraStatsLocked(testPackageName, - CallStats.CALL_TYPE_PUT_DOCUMENT); - - // count increased by 1 since cache is cleared - verify(mockPackageManager, times(2)) - .getPackageUid(eq(testPackageName), /*flags=*/ anyInt()); - assertThat(extraStats.mPackageUid).isEqualTo(testUid); - } - - private static int calculateHashCodeMd5withBigInteger(@NonNull String str) - throws NoSuchAlgorithmException { - MessageDigest md = MessageDigest.getInstance("MD5"); - md.update(str.getBytes(StandardCharsets.UTF_8)); - byte[] digest = md.digest(); - return new BigInteger(digest).intValue(); - } - - @NonNull - private PackageManager getMockPackageManager(@NonNull UserHandle user) { - PackageManager pm = mMockPackageManagers.get(user); - if (pm == null) { - pm = Mockito.mock(PackageManager.class); - mMockPackageManagers.put(user, pm); - } - return pm; - } -} 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 deleted file mode 100644 index 374642b676d2f..0000000000000 --- a/services/tests/servicestests/src/com/android/server/appsearch/visibilitystore/VisibilityStoreImplTest.java +++ /dev/null @@ -1,469 +0,0 @@ -/* - * 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.visibilitystore; - -import static android.Manifest.permission.READ_GLOBAL_APP_SEARCH_DATA; -import static android.content.pm.PackageManager.PERMISSION_DENIED; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.app.appsearch.PackageIdentifier; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.pm.PackageManager; -import android.os.UserHandle; -import android.util.ArrayMap; - -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; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.mockito.Mockito; - -import java.util.Collections; -import java.util.Map; - -public class VisibilityStoreImplTest { - /** - * Always trigger optimize in this class. OptimizeStrategy will be tested in its own test class. - */ - private static final OptimizeStrategy ALWAYS_OPTIMIZE = optimizeInfo -> true; - - @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); - private final Map mMockPackageManagers = new ArrayMap<>(); - private Context mContext; - private VisibilityStoreImpl mVisibilityStore; - private int mUid; - - @Before - public void setUp() throws Exception { - Context context = ApplicationProvider.getApplicationContext(); - mContext = new ContextWrapper(context) { - @Override - public Context createContextAsUser(UserHandle user, int flags) { - return new ContextWrapper(super.createContextAsUser(user, flags)) { - @Override - public PackageManager getPackageManager() { - return getMockPackageManager(user); - } - }; - } - - @Override - public PackageManager getPackageManager() { - return createContextAsUser(getUser(), /*flags=*/ 0).getPackageManager(); - } - }; - - // Give ourselves global query permissions - AppSearchImpl appSearchImpl = AppSearchImpl.create( - mTemporaryFolder.newFolder(), - new UnlimitedLimitConfig(), - /*initStatsBuilder=*/ null, - ALWAYS_OPTIMIZE); - mVisibilityStore = VisibilityStoreImpl.create(appSearchImpl, mContext); - mUid = mContext.getPackageManager().getPackageUid(mContext.getPackageName(), /*flags=*/ 0); - } - - /** - * Make sure that we don't conflict with any special characters that AppSearchImpl has reserved. - */ - @Test - public void testValidPackageName() { - assertThat(VisibilityStore.PACKAGE_NAME) - .doesNotContain(String.valueOf(PrefixUtil.PACKAGE_DELIMITER)); - assertThat(VisibilityStore.PACKAGE_NAME) - .doesNotContain(String.valueOf(PrefixUtil.DATABASE_DELIMITER)); - } - - /** - * Make sure that we don't conflict with any special characters that AppSearchImpl has reserved. - */ - @Test - public void testValidDatabaseName() { - assertThat(VisibilityStore.DATABASE_NAME) - .doesNotContain(String.valueOf(PrefixUtil.PACKAGE_DELIMITER)); - assertThat(VisibilityStore.DATABASE_NAME) - .doesNotContain(String.valueOf(PrefixUtil.DATABASE_DELIMITER)); - } - - @Test - public void testDoesCallerHaveSystemAccess() { - PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); - when(mockPackageManager - .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName())) - .thenReturn(PERMISSION_GRANTED); - assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName())).isTrue(); - - when(mockPackageManager - .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName())) - .thenReturn(PERMISSION_DENIED); - assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName())) - .isFalse(); - } - - @Test - public void testSetVisibility_displayedBySystem() throws Exception { - // Make sure we have global query privileges - PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); - when(mockPackageManager - .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName())) - .thenReturn(PERMISSION_GRANTED); - assertThat(mVisibilityStore.doesCallerHaveSystemAccess(mContext.getPackageName())).isTrue(); - - mVisibilityStore.setVisibility( - "package", - "database", - /*schemasNotDisplayedBySystem=*/ ImmutableSet.of( - "prefix/schema1", "prefix/schema2"), - /*schemasVisibleToPackages=*/ Collections.emptyMap()); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schema1", - mUid, - /*callerHasSystemAccess=*/ true)) - .isFalse(); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schema2", - mUid, - /*callerHasSystemAccess=*/ true)) - .isFalse(); - - // New .setVisibility() call completely overrides previous visibility settings. - // So "schema2" isn't preserved. - mVisibilityStore.setVisibility( - "package", - "database", - /*schemasNotDisplayedBySystem=*/ ImmutableSet.of( - "prefix/schema1", "prefix/schema3"), - /*schemasVisibleToPackages=*/ Collections.emptyMap()); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schema1", - mUid, - /*callerHasSystemAccess=*/ true)) - .isFalse(); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schema2", - mUid, - /*callerHasSystemAccess=*/ true)) - .isTrue(); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schema3", - mUid, - /*callerHasSystemAccess=*/ true)) - .isFalse(); - - // Everything defaults to visible again. - mVisibilityStore.setVisibility( - "package", - "database", - /*schemasNotDisplayedBySystem=*/ Collections.emptySet(), - /*schemasVisibleToPackages=*/ Collections.emptyMap()); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schema1", - mUid, - /*callerHasSystemAccess=*/ true)) - .isTrue(); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schema2", - mUid, - /*callerHasSystemAccess=*/ true)) - .isTrue(); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schema3", - mUid, - /*callerHasSystemAccess=*/ true)) - .isTrue(); - } - - @Test - public void testSetVisibility_visibleToPackages() throws Exception { - // Values for a "foo" client - String packageNameFoo = "packageFoo"; - byte[] sha256CertFoo = new byte[] {10}; - int uidFoo = 1; - - // Values for a "bar" client - String packageNameBar = "packageBar"; - byte[] sha256CertBar = new byte[] {100}; - int uidBar = 2; - - // Can't be the same value as uidFoo nor uidBar - int uidNotFooOrBar = 3; - - // Make sure none of them have global query privileges - PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); - when(mockPackageManager - .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo)) - .thenReturn(PERMISSION_DENIED); - when(mockPackageManager - .checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameBar)) - .thenReturn(PERMISSION_DENIED); - - // By default, a schema isn't package accessible. - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schemaFoo", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isFalse(); - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schemaBar", - uidBar, - /*callerHasSystemAccess=*/ false)) - .isFalse(); - - // Grant package access - mVisibilityStore.setVisibility( - "package", - "database", - /*schemasNotDisplayedBySystem=*/ Collections.emptySet(), - /*schemasVisibleToPackages=*/ ImmutableMap.of( - "prefix/schemaFoo", - ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo)), - "prefix/schemaBar", - ImmutableList.of(new PackageIdentifier(packageNameBar, sha256CertBar)))); - - // Should fail if PackageManager doesn't see that it has the proper certificate - when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt())) - .thenReturn(uidFoo); - when(mockPackageManager.hasSigningCertificate( - packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256)) - .thenReturn(false); - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schemaFoo", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isFalse(); - - // Should fail if PackageManager doesn't think the package belongs to the uid - when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt())) - .thenReturn(uidNotFooOrBar); - when(mockPackageManager.hasSigningCertificate( - packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256)) - .thenReturn(true); - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schemaFoo", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isFalse(); - - // But if uid and certificate match, then we should have access - when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt())) - .thenReturn(uidFoo); - when(mockPackageManager.hasSigningCertificate( - packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256)) - .thenReturn(true); - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schemaFoo", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isTrue(); - - when(mockPackageManager.getPackageUid(eq(packageNameBar), /*flags=*/ anyInt())) - .thenReturn(uidBar); - when(mockPackageManager.hasSigningCertificate( - packageNameBar, sha256CertBar, PackageManager.CERT_INPUT_SHA256)) - .thenReturn(true); - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schemaBar", - uidBar, - /*callerHasSystemAccess=*/ false)) - .isTrue(); - - // New .setVisibility() call completely overrides previous visibility settings. So - // "schemaBar" settings aren't preserved. - mVisibilityStore.setVisibility( - "package", - "database", - /*schemasNotDisplayedBySystem=*/ Collections.emptySet(), - /*schemasVisibleToPackages=*/ ImmutableMap.of( - "prefix/schemaFoo", - ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo)))); - - when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt())) - .thenReturn(uidFoo); - when(mockPackageManager.hasSigningCertificate( - packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256)) - .thenReturn(true); - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schemaFoo", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isTrue(); - - when(mockPackageManager.getPackageUid(eq(packageNameBar), /*flags=*/ anyInt())) - .thenReturn(uidBar); - when(mockPackageManager.hasSigningCertificate( - packageNameBar, sha256CertBar, PackageManager.CERT_INPUT_SHA256)) - .thenReturn(true); - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schemaBar", - uidBar, - /*callerHasSystemAccess=*/ false)) - .isFalse(); - } - - @Test - public void testIsSchemaSearchableByCaller_packageAccessibilityHandlesNameNotFoundException() - throws Exception { - // Values for a "foo" client - String packageNameFoo = "packageFoo"; - byte[] sha256CertFoo = new byte[] {10}; - int uidFoo = 1; - - // Pretend we can't find the Foo package. - PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); - when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt())) - .thenThrow(new PackageManager.NameNotFoundException()); - - // Make sure "foo" doesn't have global query privileges - when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo)) - .thenReturn(PERMISSION_DENIED); - - // Grant package access - mVisibilityStore.setVisibility( - "package", - "database", - /*schemasNotDisplayedBySystem=*/ Collections.emptySet(), - /*schemasVisibleToPackages=*/ ImmutableMap.of( - "prefix/schemaFoo", - ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo)))); - - // If we can't verify the Foo package that has access, assume it doesn't have access. - assertThat(mVisibilityStore.isSchemaSearchableByCaller( - "package", - "database", - "prefix/schemaFoo", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isFalse(); - } - - @Test - public void testEmptyPrefix() throws Exception { - // Values for a "foo" client - String packageNameFoo = "packageFoo"; - byte[] sha256CertFoo = new byte[] {10}; - int uidFoo = 1; - - // Set it up such that the test package has global query privileges, but "foo" doesn't. - PackageManager mockPackageManager = getMockPackageManager(mContext.getUser()); - when(mockPackageManager.checkPermission( - READ_GLOBAL_APP_SEARCH_DATA, mContext.getPackageName())) - .thenReturn(PERMISSION_GRANTED); - when(mockPackageManager.checkPermission(READ_GLOBAL_APP_SEARCH_DATA, packageNameFoo)) - .thenReturn(PERMISSION_DENIED); - - mVisibilityStore.setVisibility( - /*packageName=*/ "", - /*databaseName=*/ "", - /*schemasNotDisplayedBySystem=*/ Collections.emptySet(), - /*schemasVisibleToPackages=*/ ImmutableMap.of( - "schema", - ImmutableList.of(new PackageIdentifier(packageNameFoo, sha256CertFoo)))); - - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - /*packageName=*/ "", - /*databaseName=*/ "", - "schema", - mUid, - /*callerHasSystemAccess=*/ true)) - .isTrue(); - - when(mockPackageManager.getPackageUid(eq(packageNameFoo), /*flags=*/ anyInt())) - .thenReturn(uidFoo); - when(mockPackageManager.hasSigningCertificate( - packageNameFoo, sha256CertFoo, PackageManager.CERT_INPUT_SHA256)) - .thenReturn(true); - assertThat( - mVisibilityStore.isSchemaSearchableByCaller( - /*packageName=*/ "", - /*databaseName=*/ "", - "schema", - uidFoo, - /*callerHasSystemAccess=*/ false)) - .isTrue(); - } - - @NonNull - private PackageManager getMockPackageManager(@NonNull UserHandle user) { - PackageManager pm = mMockPackageManagers.get(user); - if (pm == null) { - pm = Mockito.mock(PackageManager.class); - mMockPackageManagers.put(user, pm); - } - return pm; - } -}