Merge "Revert "Add build rule to merge annotation zips"" into stage-aosp-master
diff --git a/apex/appsearch/Android.bp b/apex/appsearch/Android.bp
deleted file mode 100644
index 977e610..0000000
--- 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 1703369..0000000
--- 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 39a2d38..0000000
--- 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 4e5acae9..0000000
--- a/apex/appsearch/com.android.appsearch.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/apex/appsearch/com.android.appsearch.pem b/apex/appsearch/com.android.appsearch.pem
deleted file mode 100644
index 4ed5945..0000000
--- a/apex/appsearch/com.android.appsearch.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKQIBAAKCAgEAro9f/jvoIsj6ywuRmuUQS8UtprhohJitrovDMfm/T2R/WQvy
-AvUxgetyF4XvBPCDRqCsGxXCJMQOn1furrAeTmWbGHPhA0PI1Ys/qtfNMbh9THyn
-70I2c4X70CUQ+8/Y8BJ8CAB4iER/s9QtD28QLvM2BBUzRoKUSBGUYNMlYobjgRdK
-57V7yg48LkvUIg1fzIW3M5gCgOXa0u1xOadKX3m7tzCboHcXp5anfWX5PH1+okRu
-jzdI8OjtUq23qhoRw5Skz0Vbf4a+8t3kT3slF/Q7O8LoRPwpZsvIcvTyCGAqlra7
-2L2LN4H1p+u2ko3r/QmRbJn2eXW07elkyrggXMyn2rTxibQgk53wYfSavMyNd/E7
-+de/uJ60l2aPa+5KUaR8eYwchXEELdqQ+zRgSZ2711xCaY4glEj7DT6VlEEdr26x
-akX0ra7e2sVGv1um/dvSyVO5aFKKjVvo4LqhWKWO8yvDMxmDDTNatvWhY2Bhd3RA
-0hilYpWQFb9Tv5f4E0tZmfvlddgux7sw++Y/RIimBFoSyf5AezAUIFYYoYvEzytB
-muq1/ecNHr+Z2tZMxN88sJVhzRzD9tKUyXhvxOV2Lg9TIeVTWGwQqgSnHWtIe+1p
-cw8inPfYEhP4Q+3W/RlPvNdu75x8Nj2aG7bxZnhoQDRDw5ddgma27I+a8esCAwEA
-AQKCAgBsNh9I6HRAVBz8kCBkSEnw3rwtFTZdtJQ+lw+bRHpvShqT5g7R/JQDOSTS
-JkoE4uBOgT4P0E45Inz6FLW2/yDacqxR3UwJDRVMI/WFACCJCRhLuR8V+BLvTIjN
-AJ1lrPSL5rmS8E/IEcakgQyp+6ypnkXHBCl0NXCcuKEl4N7VFE+mb/0UZPHnUSnH
-fWR085uGmwH17u7mXxdnGKDPH8DALSPMLUrcj9dPIdqUpwl5kUZWa1uqVphWF98/
-GMe5oE2Q0+3TO+i7xplKz3lAOFPHZLTvmCUK1tMHkZ6ifOwpewwLwB30/5N1BpB1
-126nrWk0xKCtFUixBOHzdnLwJHKSbi7chQU5q39oAJoTfxdmAJlaG0zQHUQZ44MQ
-gemzSA7uJbtoAOAZVF1K14xbIpnfidqTB7N3RCmiJE+/Hpkq6PxgPfu5rqocPbPC
-t0FgJ4NXNmKOAuJllSlrrHATcUOhF4g5pX7tvOc8X4y7bvfwOmtw5ez3INKMF0q6
-/y0vVCi6N1Z7CTa9eY8feZ1PImk/Fkq4NInSPyx7ZE3pLYmsvuJjliFrWo9TRVae
-Dt5vvBKBOpAfhDiHkeXbX7Raj2B6c6adF4no/3SAVlAjIq1iBVjfQWyHAGUoEW1O
-u3LdHTIb6gSTLJ4AfryEKrOE+1VMlYt92GwX692KKXMaJjytSQKCAQEA3pYbl8HD
-Y++UyEN5VzWAQedT3//GDwpDfgdERe2E4smYrkVNJ2WAG2SqY1A35DIl8be3eHvl
-soaL38j48ailfDYY9tI+IlapNh+VOLej+HiOytaPlLhcv2FpSC2qZT4EiU6IBXLo
-+l6FrmD/VQXTjvoktzsDB/n1t4Dfa3Ogf+lLf1Jxr94YpEnDh18V5ofj78SplVLm
-NrzsHxAafE4Ni2a7dyWjcDYIuL7FTShT+0K4W45tRr+CGxThxu7LEe7zw4Z1IagU
-jJNtXjvDD/Zw4UTqI6RwWGZsu6UjPS6LHhOqnWqflWmFRIfMbDkuWvnGZTM9DkVg
-kk1+BNi1PECZXwKCAQEAyMOjbVo6XV3lFN0X8TpHyg/z9ar00/SE7WEJHqPSuzYT
-rSfU4vDDlaPAwkYvGi9ZKi9VM+R3CyBNxnK9Yq6NurHhhrYcAwdS/hGLT1K2o0Y8
-Pgv7gZCFb+SIwLBhlUG9otGULcBzLneqgVUqyMG6IoCjuC2LRyB71Xc2UMyg6n/f
-XpV2RTMb8f+26cgm6nj0SDAfgpr8HV6uNV80c6l1A8gq86nUWwiVAEUdmExSDe7J
-shsfWAj8RSErqDXf1BtEdPLJUSIPX5VXkzAXOXIkengwVno0vv0dBN8uraS8iQSG
-0JsJLLcw9b5kvnh6FEbE7POsIqKyCZV9VADwO6YW9QKCAQBYQsdwNqoGv6KMgozj
-8tgHyfWtVduwbQ50M+dznwpZbzz2pY5Bd/MDabhSpyVyfBwlrAa5ZM+hKc7fDu7/
-zDLKfR0LCjUPIrP4PS/LjK4dQZjFf6zxeOV2EedQcqMlgCEGXTh8iKMvXDm/+sBk
-c2n/QNs8OM8r44b2m8h78B6NefGw6/0ekn/M7V72F9M0VWAh3Cauim+09tbePmFy
-NvUR+MuPJEKZpSNyNltADCS49izqSSC1tAygNniMjHXDh6/rMS7TCLYVRARTIHlp
-o/wAp3X8aiEOPJcTFRlTElihtYSq5POgqHXqxbpek5H5CyALUvT76rCvcsDspQ3A
-dZEbAoIBAQCoLEmP5o8Rev/UdEgECB/uwWJIngYsLp3TAv/SrMRvkiL1X3JTD/+m
-L9/eXVBDjPoR/khPCcg2h77ex2qhaTrL8wnKAG6CkvYQYb3impTnPIRmLT9nDxrX
-2gY78wQrNUCXTRvlH1rcx90KLb+DH9S95ig+tdf/otRYwl27XU5GYQtJfcXuvZth
-IiWku8btjpiCh909WHpsV81yY+faI08j9d8U8WQzRYMbEMpzsyrhBO/rxBCDfDNl
-7R1W8JooYRb9KAs/bVqXZNBROW2a72RjOp6zMfdRLVHLrPC7AE32MNaFk/khfesD
-T5OwgdcxeP6oxo2hDcw5fwHXBlo2fTCpAoIBAQChgjv5AfQ50spqvHy6MNem4tV0
-L0IsxmNLsi8X2a6s4kStwUzOxDA8c/e54XabxQNZ0ERU1q+bgbG7PWC4twDMPR8i
-2DO6rgqSK4MjGOTgAoeDuy3mElFQmCLRs04Wf4jh8kPi217WFlYBynh2HmBKbh42
-JmIrLetbKEK13FXRvMkgZcX4OIDrT5TOvev4VZArU8PTRlWv3sqsKAVXjX0clGHf
-I0/2kSsr2qq1UY7JrYWZsZ9uqz2ZH0pF19a6O/Cq4uqTYoL+sYzFTSeFmChRjV1g
-ancTvTn9lcBqECDMgq5DE/p96Oxg/t8elalR6WDUlysafphVz3nTuyMTh7ka
------END RSA PRIVATE KEY-----
diff --git a/apex/appsearch/com.android.appsearch.pk8 b/apex/appsearch/com.android.appsearch.pk8
deleted file mode 100644
index 77e98b2..0000000
--- a/apex/appsearch/com.android.appsearch.pk8
+++ /dev/null
Binary files differ
diff --git a/apex/appsearch/com.android.appsearch.x509.pem b/apex/appsearch/com.android.appsearch.x509.pem
deleted file mode 100644
index e37c4b9..0000000
--- a/apex/appsearch/com.android.appsearch.x509.pem
+++ /dev/null
@@ -1,35 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIGETCCA/mgAwIBAgIUGl+MIvpEi0+TWzj0ieh6WtjXmP8wDQYJKoZIhvcNAQEL
-BQAwgZYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
-DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
-b2lkMRIwEAYDVQQDDAlhcHBzZWFyY2gxIjAgBgkqhkiG9w0BCQEWE2FuZHJvaWRA
-YW5kcm9pZC5jb20wIBcNMTkxMTIwMjMxNTM1WhgPNDc1NzEwMTYyMzE1MzVaMIGW
-MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
-bnRhaW4gVmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDES
-MBAGA1UEAwwJYXBwc2VhcmNoMSIwIAYJKoZIhvcNAQkBFhNhbmRyb2lkQGFuZHJv
-aWQuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsyPlp3q3P9Xg
-W1WhIwQiF9em9oqaGQ/3dbIxickAy591qcRbpHb4lDTZusRECfqlV215mV+lv5x4
-EhOnId3uPKBAJ/YDtL7zUW6TWL7to7zEnUqSIKTcoQzNF2EiCeGuRhrtgYvAD3HQ
-dwr4xrbSADbDArF04A49voLpsmq1fyNgl86VISiMRqoSLJnA6eghlduuOt+nf252
-6WgxDs/JrO/eK70q0+RwmWzVJ/tVr+36a65N4EHhfL4t2hdV0k0XFob7hBn7XWzC
-QrSR3jCvE3yAfAr3tq5c19/WWBA7V45nEHzXyAvBUHWubYvDi+vm/yzqU2rQwScC
-bzp4zK4CnhBHqb4gHoy0+kfFIwJ1A3GT2pl3ba/NsIYgliMtPQfkDV5PE5RTNcwH
-21ewH7vm2+spQv5Z/2TEV2lEHlp2vuAliyn2AT4u1ginr6vtBRFLmpPeziFcfB0y
-7h04GctZpX8odz+XI7aMDe47RNu9XyJX0vulntxmlDF76k8Z9DIXg02hY+yc/i7+
-2ztnj1eXL51p+HyhK5VbvJWbKkVaMQijlbuIMYNzMA6L0WHWRc2Cux9UDODMGoiC
-w09JpqudCS/95I/F1xaWJ/Kh3vKeQshHAz0hrL7v7wpjmfeXf6NGsWJGy+giCwZj
-ABtn9nFQoesgi7M1LeazD5Q/4v4AMaUCAwEAAaNTMFEwHQYDVR0OBBYEFJpHCy2Y
-3qaL6cLpE9fe53L61KEEMB8GA1UdIwQYMBaAFJpHCy2Y3qaL6cLpE9fe53L61KEE
-MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAGDYAcOhbOYcDB2K
-WDZka+FCORFFvz4nLQGE7Z9TAn1g7XusM2KbXlb2drIN6CWOFlnKQrUsNsAHrc+s
-tl+A1vC3/NfYKKBVuizPx/kHUgz3k/UIJzbzEu/uCJd86idcJoUTqC/qEJAeeQqM
-XpsNP1Yg7oyzZT8sFlUAKeDeXJ7fIDXR6nduUQ6uJXkee/5JF3VedHdgHAUsC19/
-KHhyVU3MLDUNBdAmM79+DsdVYi2Pw31jojMu95Zz1MYTRBcgQAiEw5nncr38k6ac
-Gy+JffgJR68FzI4QLBSxnDRFD2zXJ09lpP6Sjb1FVcDzk7Bi/EQDLBkrkbeLsk5F
-a0xz9VoJ3kM7Cc4R9MXN4ZWuePjdJwgasnHmllsXn45R9odgJgmfzuUwtgNw/XKQ
-QcQl7Q9QUrBCqIoHijxscUZCBSmIHVNBBDckRAmSXHeWMRlO3uBR4IA/Jfrt//4f
-uc7CNUp+LQ6EzBXJOVFrXRtau6Oj+jM1+fzxKo1uV2+T+GdVEE5jeF/6nB3qna6h
-2NmyLqbqeqp2QxgzBWSGy8Ugs6zg4wItJBqOoRLKKFxTJu5OAzJ4fUA+g7WFXNhR
-kG56SJ863LZoORKHWE72oXYeIW98Tq0qKLH3NzH5L4tfX8DeBTq+APezHetH1ljA
-D0avPy62g0i643bbpwZgezBgRIKL
------END CERTIFICATE-----
diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp
deleted file mode 100644
index 8964668..0000000
--- a/apex/appsearch/framework/Android.bp
+++ /dev/null
@@ -1,68 +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"],
-}
-
-filegroup {
- name: "framework-appsearch-sources",
- srcs: [
- ":framework-appsearch-internal-sources",
- ":framework-appsearch-external-sources",
- ],
- visibility: ["//frameworks/base"],
-}
-
-filegroup {
- name: "framework-appsearch-internal-sources",
- srcs: [
- "java/**/*.java",
- "java/**/*.aidl",
- ],
- exclude_srcs: [":framework-appsearch-external-sources"],
- path: "java",
-}
-
-filegroup {
- name: "framework-appsearch-external-sources",
- srcs: [
- "java/external/**/*.java",
- "java/external/**/*.aidl",
- ],
- path: "java/external",
-}
-
-java_sdk_library {
- name: "framework-appsearch",
- srcs: [":framework-appsearch-sources"],
- sdk_version: "module_current",
- static_libs: [
- // This list must be kept in sync with jarjar.txt
- "modules-utils-preconditions",
- ],
- defaults: ["framework-module-defaults"],
- permitted_packages: ["android.app.appsearch"],
- jarjar_rules: "jarjar-rules.txt",
- apex_available: ["com.android.appsearch"],
- impl_library_visibility: [
- "//frameworks/base/apex/appsearch/service",
- ],
- unsafe_ignore_missing_latest_api: true, // TODO(b/146218515) should be removed
-}
diff --git a/apex/appsearch/framework/api/current.txt b/apex/appsearch/framework/api/current.txt
deleted file mode 100644
index 761405c..0000000
--- a/apex/appsearch/framework/api/current.txt
+++ /dev/null
@@ -1,460 +0,0 @@
-// Signature format: 2.0
-package android.app.appsearch {
-
- public final class AppSearchBatchResult<KeyType, ValueType> {
- method @NonNull public java.util.Map<KeyType,android.app.appsearch.AppSearchResult<ValueType>> getAll();
- method @NonNull public java.util.Map<KeyType,android.app.appsearch.AppSearchResult<ValueType>> getFailures();
- method @NonNull public java.util.Map<KeyType,ValueType> getSuccesses();
- method public boolean isSuccess();
- }
-
- public static final class AppSearchBatchResult.Builder<KeyType, ValueType> {
- ctor public AppSearchBatchResult.Builder();
- method @NonNull public android.app.appsearch.AppSearchBatchResult<KeyType,ValueType> build();
- method @NonNull public android.app.appsearch.AppSearchBatchResult.Builder<KeyType,ValueType> setFailure(@NonNull KeyType, int, @Nullable String);
- method @NonNull public android.app.appsearch.AppSearchBatchResult.Builder<KeyType,ValueType> setResult(@NonNull KeyType, @NonNull android.app.appsearch.AppSearchResult<ValueType>);
- method @NonNull public android.app.appsearch.AppSearchBatchResult.Builder<KeyType,ValueType> setSuccess(@NonNull KeyType, @Nullable ValueType);
- }
-
- public class AppSearchManager {
- method public void createGlobalSearchSession(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.GlobalSearchSession>>);
- method public void createSearchSession(@NonNull android.app.appsearch.AppSearchManager.SearchContext, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.AppSearchSession>>);
- }
-
- 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<ValueType> {
- method @Nullable public String getErrorMessage();
- method public int getResultCode();
- method @Nullable public ValueType getResultValue();
- method public boolean isSuccess();
- method @NonNull public static <ValueType> android.app.appsearch.AppSearchResult<ValueType> newFailedResult(int, @Nullable String);
- method @NonNull public static <ValueType> android.app.appsearch.AppSearchResult<ValueType> 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<android.app.appsearch.AppSearchSchema.PropertyConfig> 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<java.lang.String,android.app.appsearch.GenericDocument>);
- method public void getNamespaces(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.util.Set<java.lang.String>>>);
- method public void getSchema(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.GetSchemaResponse>>);
- method public void getStorageInfo(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<android.app.appsearch.StorageInfo>>);
- method public void put(@NonNull android.app.appsearch.PutDocumentsRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
- method public void remove(@NonNull android.app.appsearch.RemoveByDocumentIdRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.appsearch.BatchResultCallback<java.lang.String,java.lang.Void>);
- method public void remove(@NonNull String, @NonNull android.app.appsearch.SearchSpec, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
- method public void reportUsage(@NonNull android.app.appsearch.ReportUsageRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.appsearch.AppSearchResult<java.lang.Void>>);
- 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<android.app.appsearch.AppSearchResult<android.app.appsearch.SetSchemaResponse>>);
- }
-
- public interface BatchResultCallback<KeyType, ValueType> {
- method public void onResult(@NonNull android.app.appsearch.AppSearchBatchResult<KeyType,ValueType>);
- 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<java.lang.String> 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<BuilderType extends android.app.appsearch.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<java.lang.String> getIds();
- method @NonNull public String getNamespace();
- method @NonNull public java.util.Map<java.lang.String,java.util.List<java.lang.String>> 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<java.lang.String>);
- method @NonNull public android.app.appsearch.GetByDocumentIdRequest.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
- method @NonNull public android.app.appsearch.GetByDocumentIdRequest build();
- }
-
- public final class GetSchemaResponse {
- method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> 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<android.app.appsearch.AppSearchResult<java.lang.Void>>);
- 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<android.app.appsearch.GenericDocument> 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<? extends android.app.appsearch.GenericDocument>);
- method @NonNull public android.app.appsearch.PutDocumentsRequest build();
- }
-
- public final class RemoveByDocumentIdRequest {
- method @NonNull public java.util.Set<java.lang.String> 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<java.lang.String>);
- 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<android.app.appsearch.SearchResult.MatchInfo> 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<android.app.appsearch.AppSearchResult<java.util.List<android.app.appsearch.SearchResult>>>);
- }
-
- public final class SearchSpec {
- method @NonNull public java.util.List<java.lang.String> getFilterNamespaces();
- method @NonNull public java.util.List<java.lang.String> getFilterPackageNames();
- method @NonNull public java.util.List<java.lang.String> getFilterSchemas();
- method public int getMaxSnippetSize();
- method public int getOrder();
- method @NonNull public java.util.Map<java.lang.String,java.util.List<java.lang.String>> 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<java.lang.String>);
- 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<java.lang.String>);
- 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<java.lang.String>);
- method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
- 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<java.lang.String,android.app.appsearch.Migrator> getMigrators();
- method @NonNull public java.util.Set<android.app.appsearch.AppSearchSchema> getSchemas();
- method @NonNull public java.util.Set<java.lang.String> getSchemasNotDisplayedBySystem();
- method @NonNull public java.util.Map<java.lang.String,java.util.Set<android.app.appsearch.PackageIdentifier>> 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<android.app.appsearch.AppSearchSchema>);
- method @NonNull public android.app.appsearch.SetSchemaRequest build();
- method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setForceOverride(boolean);
- method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrator(@NonNull String, @NonNull android.app.appsearch.Migrator);
- method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setMigrators(@NonNull java.util.Map<java.lang.String,android.app.appsearch.Migrator>);
- method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeDisplayedBySystem(@NonNull String, boolean);
- method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setSchemaTypeVisibilityForPackage(@NonNull String, boolean, @NonNull android.app.appsearch.PackageIdentifier);
- method @NonNull public android.app.appsearch.SetSchemaRequest.Builder setVersion(@IntRange(from=1) int);
- }
-
- public class SetSchemaResponse {
- method @NonNull public java.util.Set<java.lang.String> getDeletedTypes();
- method @NonNull public java.util.Set<java.lang.String> getIncompatibleTypes();
- method @NonNull public java.util.Set<java.lang.String> getMigratedTypes();
- method @NonNull public java.util.List<android.app.appsearch.SetSchemaResponse.MigrationFailure> 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<java.lang.String>);
- method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleType(@NonNull String);
- method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addIncompatibleTypes(@NonNull java.util.Collection<java.lang.String>);
- method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedType(@NonNull String);
- method @NonNull public android.app.appsearch.SetSchemaResponse.Builder addMigratedTypes(@NonNull java.util.Collection<java.lang.String>);
- 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<android.app.appsearch.SetSchemaResponse.MigrationFailure>);
- 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<java.lang.Void> 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 <T> android.app.appsearch.AppSearchResult<T> 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 d802177..0000000
--- 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 d802177..0000000
--- 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 d802177..0000000
--- 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 4a6194e..0000000
--- 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 d802177..0000000
--- 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 50c3ee4..0000000
--- 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 12188f8..0000000
--- 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 fb1cccf..0000000
--- 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.
- *
- * <p>AppSearch is an offline, on-device search library for managing structured data featuring:
- *
- * <ul>
- * <li>APIs to index and retrieve data via full-text search.
- * <li>An API for applications to explicitly grant read-access permission of their data to other
- * applications.
- * <b>See: {@link SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage}</b>
- * <li>An API for applications to opt into or out of having their data displayed on System UI
- * surfaces by the System-designated global querier.
- * <b>See: {@link SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem}</b>
- * </ul>
- *
- * <p>Applications create a database by opening an {@link AppSearchSession}.
- *
- * <p>Example:
- *
- * <pre>
- * AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
- *
- * AppSearchManager.SearchContext searchContext = new AppSearchManager.SearchContext.Builder().
- * setDatabaseName(dbName).build());
- * appSearchManager.createSearchSession(searchContext, mExecutor, appSearchSessionResult -> {
- * mAppSearchSession = appSearchSessionResult.getResultValue();
- * });</pre>
- *
- * <p>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.
- *
- * <p>Example:
- *
- * <pre>
- * 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.
- * }
- * });</pre>
- *
- * <p>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.
- *
- * <p>Once the schema has been set, {@link GenericDocument} objects can be put into the database and
- * indexed by calling {@link AppSearchSession#put}.
- *
- * <p>Example:
- *
- * <pre>
- * // 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.
- * }
- * });</pre>
- *
- * <p>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}.
- *
- * <p>Alternatively, {@link AppSearchSession#getByDocumentId} can be called to retrieve documents by
- * namespace and ID.
- *
- * <p>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.
- *
- * <p>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}.
- *
- * <p>{@link AppSearchSession} will create or open a database under the given name.
- *
- * <p>Databases with different names are fully separate with distinct types, namespaces,
- * and data.
- *
- * <p>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}.
- *
- * <p>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<AppSearchResult<AppSearchSession>> callback) {
- Objects.requireNonNull(searchContext);
- Objects.requireNonNull(executor);
- Objects.requireNonNull(callback);
- AppSearchSession.createSearchSession(
- searchContext,
- mService,
- mContext.getUser(),
- getPackageName(),
- executor,
- callback);
- }
-
- /**
- * Creates a new {@link GlobalSearchSession}.
- *
- * <p>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<AppSearchResult<GlobalSearchSession>> 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 7dc527a..0000000
--- 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 c738504..0000000
--- 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.
- *
- * <p>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<String> mDestinationTypes;
- private boolean mAreDocumentsMigrated = false;
-
- AppSearchMigrationHelper(@NonNull IAppSearchManager service,
- @NonNull UserHandle userHandle,
- @NonNull String packageName,
- @NonNull String databaseName,
- @NonNull Set<AppSearchSchema> 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}.
- *
- * <p>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<AppSearchResult<Void>> 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<Void> 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.
- *
- * <p> 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<SetSchemaResponse> putMigratedDocuments(
- @NonNull SetSchemaResponse.Builder responseBuilder) {
- if (!mAreDocumentsMigrated) {
- return AppSearchResult.newSuccessfulResult(responseBuilder.build());
- }
- try (ParcelFileDescriptor fileDescriptor =
- ParcelFileDescriptor.open(mMigratedFile, MODE_READ_ONLY)) {
- CompletableFuture<AppSearchResult<List<Bundle>>> future = new CompletableFuture<>();
- mService.putDocumentsFromFile(mPackageName, mDatabaseName, fileDescriptor, mUserHandle,
- new IAppSearchResultCallback.Stub() {
- @Override
- public void onResult(AppSearchResultParcel resultParcel) {
- future.complete(resultParcel.getResult());
- }
- });
- AppSearchResult<List<Bundle>> result = future.get();
- if (!result.isSuccess()) {
- return AppSearchResult.newFailedResult(result);
- }
- List<Bundle> 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}.
- *
- * <p>Transforms those {@link GenericDocument}s to the final version.
- *
- * <p>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 82b6d62..0000000
--- 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.
- *
- * <p>An {@link AppSearchSession} instance provides access to database operations such as
- * setting a schema, adding documents, and searching.
- *
- * <p>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<AppSearchResult<AppSearchSession>> 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<AppSearchResult<AppSearchSession>> callback) {
- try {
- mService.initialize(
- mPackageName,
- mUserHandle,
- /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
- new IAppSearchResultCallback.Stub() {
- @Override
- public void onResult(AppSearchResultParcel resultParcel) {
- executor.execute(() -> {
- AppSearchResult<Void> 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.
- *
- * <p>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<AppSearchResult<SetSchemaResponse>> callback) {
- Objects.requireNonNull(request);
- Objects.requireNonNull(workExecutor);
- Objects.requireNonNull(callbackExecutor);
- Objects.requireNonNull(callback);
- Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
- List<Bundle> schemaBundles = new ArrayList<>(request.getSchemas().size());
- for (AppSearchSchema schema : request.getSchemas()) {
- schemaBundles.add(schema.getBundle());
- }
- Map<String, List<Bundle>> schemasVisibleToPackagesBundles =
- new ArrayMap<>(request.getSchemasVisibleToPackagesInternal().size());
- for (Map.Entry<String, Set<PackageIdentifier>> entry :
- request.getSchemasVisibleToPackagesInternal().entrySet()) {
- List<Bundle> 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<AppSearchResult<GetSchemaResponse>> 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<Bundle> 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<AppSearchResult<Set<String>>> 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<List<String>> result = resultParcel.getResult();
- if (result.isSuccess()) {
- Set<String> 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.
- *
- * <p>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<String, Void> callback) {
- Objects.requireNonNull(request);
- Objects.requireNonNull(executor);
- Objects.requireNonNull(callback);
- Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
- List<GenericDocument> documents = request.getGenericDocuments();
- List<Bundle> 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<String, GenericDocument> 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<String, Bundle> result =
- resultParcel.getResult();
- AppSearchBatchResult.Builder<String, GenericDocument>
- documentResultBuilder =
- new AppSearchBatchResult.Builder<>();
-
- // Translate successful results
- for (Map.Entry<String, Bundle> 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<String, AppSearchResult<Bundle>> bundleEntry :
- ((Map<String, AppSearchResult<Bundle>>)
- 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.
- *
- * <p>Query strings can be empty, contain one term with no operators, or contain multiple terms
- * and operators.
- *
- * <p>For query strings that are empty, all documents that match the {@link SearchSpec} will be
- * returned.
- *
- * <p>For query strings with a single term and no operators, documents that match the provided
- * query string and {@link SearchSpec} will be returned.
- *
- * <p>The following operators are supported:
- *
- * <ul>
- * <li>AND (implicit)
- * <p>AND is an operator that matches documents that contain <i>all</i> provided terms.
- * <p><b>NOTE:</b> 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".
- * <p>Example: "apple AND banana" matches documents that contain the terms "apple", "and",
- * "banana".
- * <p>Example: "apple banana" matches documents that contain both "apple" and "banana".
- * <p>Example: "apple banana cherry" matches documents that contain "apple", "banana", and
- * "cherry".
- * <li>OR
- * <p>OR is an operator that matches documents that contain <i>any</i> provided term.
- * <p>Example: "apple OR banana" matches documents that contain either "apple" or
- * "banana".
- * <p>Example: "apple OR banana OR cherry" matches documents that contain any of "apple",
- * "banana", or "cherry".
- * <li>Exclusion (-)
- * <p>Exclusion (-) is an operator that matches documents that <i>do not</i> contain the
- * provided term.
- * <p>Example: "-apple" matches documents that do not contain "apple".
- * <li>Grouped Terms
- * <p>For queries that require multiple operators and terms, terms can be grouped into
- * subqueries. Subqueries are contained within an open "(" and close ")" parenthesis.
- * <p>Example: "(donut OR bagel) (coffee OR tea)" matches documents that contain either
- * "donut" or "bagel" and either "coffee" or "tea".
- * <li>Property Restricts
- * <p>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.
- * <p>Example: "subject:important" matches documents that contain the term "important" in
- * the "subject" property.
- * </ul>
- *
- * <p>Additional search specifications, such as filtering by {@link AppSearchSchema} type or
- * adding projection, can be set by calling the corresponding {@link SearchSpec.Builder} setter.
- *
- * <p>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.
- *
- * <p>A usage report represents an event in which a user interacted with or viewed a document.
- *
- * <p>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.
- *
- * <p>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<AppSearchResult<Void>> 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.
- *
- * <p>Removed documents will no longer be surfaced by {@link #search} or {@link
- * #getByDocumentId} calls.
- *
- * <p>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<String, Void> 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}.
- *
- * <p>An empty {@code queryExpression} matches all documents.
- *
- * <p>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<AppSearchResult<Void>> 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.
- *
- * <p>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<AppSearchResult<StorageInfo>> 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<Bundle> 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.
- *
- * <p>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<Bundle> schemaBundles,
- @NonNull Map<String, List<Bundle>> schemasVisibleToPackagesBundles,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull Consumer<AppSearchResult<SetSchemaResponse>> 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<Bundle> 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.
- *
- * <p>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<Bundle> schemaBundles,
- @NonNull Map<String, List<Bundle>> schemasVisibleToPackagesBundles,
- @NonNull Executor workExecutor,
- @NonNull @CallbackExecutor Executor callbackExecutor,
- @NonNull Consumer<AppSearchResult<SetSchemaResponse>> callback) {
- workExecutor.execute(() -> {
- try {
- // Migration process
- // 1. Validate and retrieve all active migrators.
- CompletableFuture<AppSearchResult<GetSchemaResponse>> getSchemaFuture =
- new CompletableFuture<>();
- getSchema(callbackExecutor, getSchemaFuture::complete);
- AppSearchResult<GetSchemaResponse> 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<String, Migrator> 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<AppSearchResult<Bundle>> 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<Bundle> 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<String, Migrator> 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<AppSearchResult<Bundle>> 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<Bundle> 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<SetSchemaResponse> 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}.
- *
- * <p>The {@link AppSearchResult} generally comes from
- * {@link IAppSearchBatchResultCallback#onSystemError}.
- *
- * <p>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 28f8a7a..0000000
--- 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 <KeyType> The type of the keys for {@link AppSearchBatchResult#getSuccesses} and
- * {@link AppSearchBatchResult#getFailures}.
- * @param <ValueType> The type of result objects associated with the keys.
- */
-public interface BatchResultCallback<KeyType, ValueType> {
-
- /**
- * Called when {@link AppSearchBatchResult} results are ready.
- *
- * @param result The result of the executed request.
- */
- void onResult(@NonNull AppSearchBatchResult<KeyType, ValueType> result);
-
- /**
- * Called when a system error occurs.
- *
- * <p>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).
- *
- * <p>The error is not expected to be recoverable and there is no specific recommended action
- * other than displaying a permanent message to the user.
- *
- * <p>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 130e442..0000000
--- 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.
- *
- * <p>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<AppSearchResult<GlobalSearchSession>> 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<AppSearchResult<GlobalSearchSession>> callback) {
- try {
- mService.initialize(
- mPackageName,
- mUserHandle,
- /*binderCallStartTimeMillis=*/ SystemClock.elapsedRealtime(),
- new IAppSearchResultCallback.Stub() {
- @Override
- public void onResult(AppSearchResultParcel resultParcel) {
- executor.execute(() -> {
- AppSearchResult<Void> 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.
- *
- * <p>Applications can be granted access to documents by specifying {@link
- * SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} when building a schema.
- *
- * <p>Document access can also be granted to system UIs by specifying {@link
- * SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} when building a schema.
- *
- * <p>See {@link AppSearchSession#search} for a detailed explanation on forming a query string.
- *
- * <p>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.
- *
- * <p>See {@link AppSearchSession#reportUsage} for a general description of document usage, as
- * well as an API that can be used by the app itself.
- *
- * <p>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<AppSearchResult<Void>> 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 6dfa01f..0000000
--- 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.
- *
- * <p>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}.
- *
- * <p>To fetch a page of results, call {@link #getNextPage}.
- *
- * <p>All instances of {@link SearchResults} must call {@link SearchResults#close()} after the
- * results are fetched.
- *
- * <p>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.
- *
- * <p>The page size is configured by {@link SearchSpec.Builder#setResultCountPerPage}.
- *
- * <p>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<AppSearchResult<List<SearchResult>>> 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<AppSearchResult<List<SearchResult>>> callback) {
- return new IAppSearchResultCallback.Stub() {
- @Override
- public void onResult(AppSearchResultParcel resultParcel) {
- executor.execute(() -> invokeCallback(resultParcel.getResult(), callback));
- }
- };
- }
-
- private void invokeCallback(
- @NonNull AppSearchResult<Bundle> searchResultPageResult,
- @NonNull Consumer<AppSearchResult<List<SearchResult>>> 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 89908cd..0000000
--- 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<ValueType>;
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 b0cc10c..0000000
--- 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}.
- *
- * <p>{@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 <ValueType> The type of result object for successful calls. Must be a parcelable type.
- * @hide
- */
-public final class AppSearchBatchResultParcel<ValueType> implements Parcelable {
- private final AppSearchBatchResult<String, ValueType> mResult;
-
- /** Creates a new {@link AppSearchBatchResultParcel} from the given result. */
- public AppSearchBatchResultParcel(@NonNull AppSearchBatchResult<String, ValueType> result) {
- mResult = Objects.requireNonNull(result);
- }
-
- private AppSearchBatchResultParcel(@NonNull Parcel in) {
- Bundle bundle = in.readBundle();
- AppSearchBatchResult.Builder<String, ValueType> builder =
- new AppSearchBatchResult.Builder<>();
- for (String key : bundle.keySet()) {
- AppSearchResultParcel<ValueType> resultParcel = bundle.getParcelable(key);
- builder.setResult(key, resultParcel.getResult());
- }
- mResult = builder.build();
- }
-
- @NonNull
- public AppSearchBatchResult<String, ValueType> getResult() {
- return mResult;
- }
-
- /** @hide */
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- Bundle bundle = new Bundle();
- for (Map.Entry<String, AppSearchResult<ValueType>> 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<AppSearchBatchResultParcel<?>> CREATOR =
- new Creator<AppSearchBatchResultParcel<?>>() {
- @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 4e35bd5..0000000
--- 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<ValueType>;
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 8b137d3..0000000
--- 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}.
- *
- * <p>{@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 <ValueType> The type of result object for successful calls. Must be a parcelable type.
- * @hide
- */
-public final class AppSearchResultParcel<ValueType> implements Parcelable {
- private final AppSearchResult<ValueType> mResult;
-
- /** Creates a new {@link AppSearchResultParcel} from the given result. */
- public AppSearchResultParcel(@NonNull AppSearchResult<ValueType> 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<ValueType> 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<AppSearchResultParcel<?>> CREATOR =
- new Creator<AppSearchResultParcel<?>>() {
- @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 1fe19cc..0000000
--- 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 a2f545f..0000000
--- 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<Bundle> schemaBundles,
- in List<String> schemasNotDisplayedBySystem,
- in Map<String, List<Bundle>> 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<Bundle> 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<String> ids,
- in Map<String, List<String>> 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.
- *
- * <p>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.
- *
- * <p>A usage report represents an event in which a user interacted with or viewed a document.
- *
- * <p>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.
- *
- * <p>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<String> 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 097f0d1..0000000
--- 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 d493a1c..0000000
--- 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.
- *
- * <p>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.
- *
- * <p>Alternatively, {@link #getAll()} returns a map of keys to {@link AppSearchResult} objects for
- * both successes and failures.
- *
- * @param <KeyType> The type of the keys for which the results will be reported.
- * @param <ValueType> The type of the result objects for successful results.
- * @see AppSearchSession#put
- * @see AppSearchSession#getByDocumentId
- * @see AppSearchSession#remove
- */
-public final class AppSearchBatchResult<KeyType, ValueType> {
- @NonNull private final Map<KeyType, ValueType> mSuccesses;
- @NonNull private final Map<KeyType, AppSearchResult<ValueType>> mFailures;
- @NonNull private final Map<KeyType, AppSearchResult<ValueType>> mAll;
-
- AppSearchBatchResult(
- @NonNull Map<KeyType, ValueType> successes,
- @NonNull Map<KeyType, AppSearchResult<ValueType>> failures,
- @NonNull Map<KeyType, AppSearchResult<ValueType>> 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.
- *
- * <p>Example: {@link AppSearchSession#getByDocumentId} returns an {@link AppSearchBatchResult}.
- * Each key (the document ID, of {@code String} type) will map to a {@link GenericDocument}
- * object.
- *
- * <p>The values of the {@link Map} will not be {@code null}.
- */
- @NonNull
- public Map<KeyType, ValueType> getSuccesses() {
- return Collections.unmodifiableMap(mSuccesses);
- }
-
- /**
- * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all failed
- * individual results.
- *
- * <p>The values of the {@link Map} will not be {@code null}.
- */
- @NonNull
- public Map<KeyType, AppSearchResult<ValueType>> getFailures() {
- return Collections.unmodifiableMap(mFailures);
- }
-
- /**
- * Returns a {@link Map} of keys mapped to instances of {@link AppSearchResult} for all
- * individual results.
- *
- * <p>The values of the {@link Map} will not be {@code null}.
- */
- @NonNull
- public Map<KeyType, AppSearchResult<ValueType>> getAll() {
- return Collections.unmodifiableMap(mAll);
- }
-
- @Override
- @NonNull
- public String toString() {
- return "{\n successes: " + mSuccesses + "\n failures: " + mFailures + "\n}";
- }
-
- /**
- * Builder for {@link AppSearchBatchResult} objects.
- *
- * @param <KeyType> The type of the keys for which the results will be reported.
- * @param <ValueType> The type of the result objects for successful results.
- */
- public static final class Builder<KeyType, ValueType> {
- private ArrayMap<KeyType, ValueType> mSuccesses = new ArrayMap<>();
- private ArrayMap<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>();
- private ArrayMap<KeyType, AppSearchResult<ValueType>> mAll = new ArrayMap<>();
- private boolean mBuilt = false;
-
- /**
- * Associates the {@code key} with the provided successful return value.
- *
- * <p>Any previous mapping for a key, whether success or failure, is deleted.
- *
- * <p>This is a convenience function which is equivalent to {@code setResult(key,
- * AppSearchResult.newSuccessfulResult(value))}.
- *
- * @param key The key to associate the result with; usually corresponds to some identifier
- * from the input like an ID or name.
- * @param value An optional value to associate with the successful result of the operation
- * being performed.
- */
- @SuppressWarnings("MissingGetterMatchingBuilder") // See getSuccesses
- @NonNull
- public Builder<KeyType, ValueType> 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.
- *
- * <p>Any previous mapping for a key, whether success or failure, is deleted.
- *
- * <p>This is a convenience function which is equivalent to {@code setResult(key,
- * AppSearchResult.newFailedResult(resultCode, errorMessage))}.
- *
- * @param key The key to associate the result with; usually corresponds to some identifier
- * from the input like an ID or name.
- * @param resultCode One of the constants documented in {@link
- * AppSearchResult#getResultCode}.
- * @param errorMessage An optional string describing the reason or nature of the failure.
- */
- @SuppressWarnings("MissingGetterMatchingBuilder") // See getFailures
- @NonNull
- public Builder<KeyType, ValueType> 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}.
- *
- * <p>Any previous mapping for a key, whether success or failure, is deleted.
- *
- * @param key The key to associate the result with; usually corresponds to some identifier
- * from the input like an ID or name.
- * @param result The result to associate with the key.
- */
- @SuppressWarnings("MissingGetterMatchingBuilder") // See getAll
- @NonNull
- public Builder<KeyType, ValueType> setResult(
- @NonNull KeyType key, @NonNull AppSearchResult<ValueType> 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<KeyType, ValueType> 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 b1cb132..0000000
--- 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 <ValueType> The type of result object for successful calls.
- */
-public final class AppSearchResult<ValueType> {
- 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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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 <ValueType> AppSearchResult<ValueType> 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 <ValueType> AppSearchResult<ValueType> 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 <ValueType> AppSearchResult<ValueType> 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 <ValueType> AppSearchResult<ValueType> 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 2e04d71..0000000
--- 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.
- *
- * <p>For example, an e-mail message or a music recording could be a schema type.
- *
- * <p>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.
- *
- * <p>This method creates a new list when called.
- */
- @NonNull
- @SuppressWarnings("MixedMutabilityReturnType")
- public List<PropertyConfig> getProperties() {
- ArrayList<Bundle> propertyBundles =
- mBundle.getParcelableArrayList(AppSearchSchema.PROPERTIES_FIELD);
- if (propertyBundles.isEmpty()) {
- return Collections.emptyList();
- }
- List<PropertyConfig> 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<Bundle> mPropertyBundles = new ArrayList<>();
- private final Set<String> 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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>Ex. A property with "fool" <b>should</b> 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.
- *
- * <p>It is only valid for tokenizer_type to be 'NONE' if {@link #getIndexingType} is {@link
- * #INDEXING_TYPE_NONE}.
- */
- public static final int TOKENIZER_TYPE_NONE = 0;
-
- /**
- * Tokenization for plain text. This value indicates that tokens should be extracted from
- * this property based on word breaks. Segments of whitespace and punctuation are not
- * considered tokens.
- *
- * <p>Ex. A property with "foo bar. baz." will produce tokens for "foo", "bar" and "baz".
- * The segments " " and "." will not be considered tokens.
- *
- * <p>It is only valid for tokenizer_type to be 'PLAIN' if {@link #getIndexingType} is
- * {@link #INDEXING_TYPE_EXACT_TERMS} or {@link #INDEXING_TYPE_PREFIXES}.
- */
- public static final int TOKENIZER_TYPE_PLAIN = 1;
-
- StringPropertyConfig(@NonNull Bundle bundle) {
- 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).
- *
- * <p>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.
- *
- * <p>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).
- *
- * <p>If this method is not called, the default indexing type is {@link
- * StringPropertyConfig#TOKENIZER_TYPE_NONE}, so that it is not tokenized.
- *
- * <p>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.
- *
- * <p>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).
- *
- * <p>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).
- *
- * <p>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).
- *
- * <p>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).
- *
- * <p>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.
- *
- * <p>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).
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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 4d27519..0000000
--- 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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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}.
- *
- * <p>The score is a query-independent measure of the document's quality, relative to other
- * {@link GenericDocument} objects of the same {@link AppSearchSchema} type.
- *
- * <p>Results may be sorted by score using {@link SearchSpec.Builder#setRankingStrategy}.
- * Documents with higher scores are considered better than documents with lower scores.
- *
- * <p>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<String> getPropertyNames() {
- return Collections.unmodifiableSet(mProperties.keySet());
- }
-
- /**
- * Retrieves the property value with the given path as {@link Object}.
- *
- * <p>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]}.
- *
- * <p>For example, given the following {@link GenericDocument}:
- *
- * <pre>
- * (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"
- * }
- * </pre>
- *
- * <p>Here are some example paths and their results:
- *
- * <ul>
- * <li>{@code "from"} returns {@code "sender@example.com"} as a {@link String} array with one
- * element
- * <li>{@code "to"} returns the two nested documents containing contact information as a
- * {@link GenericDocument} array with two elements
- * <li>{@code "to[1]"} returns the second nested document containing Marie Curie's contact
- * information as a {@link GenericDocument} array with one element
- * <li>{@code "to[1].email"} returns {@code "curie@example.com"}
- * <li>{@code "to[100].email"} returns {@code null} as this particular document does not have
- * that many elements in its {@code "to"} array.
- * <li>{@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.
- * </ul>
- *
- * <p>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<Bundle> where each Bundle contains just a single
- // entry: BYTE_ARRAY_FIELD -> byte[].
- @SuppressWarnings("unchecked")
- List<Bundle> bundles = (List<Bundle>) 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.
- *
- * <p>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<Bundle> bundles = (List<Bundle>) 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<Object> 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<Bundle>}, 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<Object> 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<Bundle> result = new ArrayList<>(length);
- for (int i = 0; i < accumulator.size(); i++) {
- @SuppressWarnings("unchecked")
- List<Bundle> castValue = (List<Bundle>) 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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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> T safeCastProperty(
- @NonNull String path, @Nullable Object value, @NonNull Class<T> 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}.
- *
- * <p>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<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 <BuilderType> 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<BuilderType extends Builder> {
- private Bundle mBundle;
- private Bundle mProperties;
- private final BuilderType mBuilderTypeInstance;
- private boolean mBuilt = false;
-
- /**
- * Creates a new {@link GenericDocument.Builder}.
- *
- * <p>Document IDs are unique within a namespace.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>Document IDs are unique within a namespace.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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}.
- *
- * <p>The score is a query-independent measure of the document's quality, relative to other
- * {@link GenericDocument} objects of the same {@link AppSearchSchema} type.
- *
- * <p>Results may be sorted by score using {@link SearchSpec.Builder#setRankingStrategy}.
- * Documents with higher scores are considered better than documents with lower scores.
- *
- * <p>Any non-negative integer can be used a score. By default, scores are set to 0.
- *
- * @param score any non-negative {@code int} representing the document's score.
- */
- @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.
- *
- * <p>This should be set using a value obtained from the {@link System#currentTimeMillis}
- * time base.
- *
- * <p>If this method is not called, this will be set to the time the object is built.
- *
- * @param creationTimestampMillis a creation timestamp in milliseconds.
- */
- @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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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}.
- *
- * <p>Bundle doesn't support for two dimension array byte[][], we are converting byte[][]
- * into ArrayList<Bundle>, and each elements will contain a one dimension byte[].
- */
- private void putInPropertyBundle(@NonNull String name, @NonNull byte[][] values) {
- ArrayList<Bundle> 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 558899e..0000000
--- 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<String> mIds;
- private final Map<String, List<String>> mTypePropertyPathsMap;
-
- GetByDocumentIdRequest(
- @NonNull String namespace,
- @NonNull Set<String> ids,
- @NonNull Map<String, List<String>> 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<String> getIds() {
- return Collections.unmodifiableSet(mIds);
- }
-
- /**
- * Returns a map from schema type to property paths to be used for projection.
- *
- * <p>If the map is empty, then all properties will be retrieved for all results.
- *
- * <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this
- * function, rather than calling it multiple times.
- */
- @NonNull
- public Map<String, List<String>> getProjections() {
- Map<String, List<String>> copy = new ArrayMap<>();
- for (Map.Entry<String, List<String>> 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.
- *
- * <p>If the map is empty, then all properties will be retrieved for all results.
- *
- * <p>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<String, List<String>> getProjectionsInternal() {
- return mTypePropertyPathsMap;
- }
-
- /** Builder for {@link GetByDocumentIdRequest} objects. */
- public static final class Builder {
- private final String mNamespace;
- private ArraySet<String> mIds = new ArraySet<>();
- private ArrayMap<String, List<String>> 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<String> 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.
- *
- * <p>If no property paths are added for a particular type, then all properties of results
- * of that type will be retrieved.
- *
- * <p>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<String> propertyPaths) {
- Objects.requireNonNull(schemaType);
- Objects.requireNonNull(propertyPaths);
- resetIfBuilt();
- List<String> 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 018f16d..0000000
--- 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.
- *
- * <p>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}.
- *
- * <p>It is inefficient to call this method repeatedly.
- */
- @NonNull
- public Set<AppSearchSchema> getSchemas() {
- ArrayList<Bundle> schemaBundles = mBundle.getParcelableArrayList(SCHEMAS_FIELD);
- Set<AppSearchSchema> 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<Bundle> mSchemaBundles = new ArrayList<>();
- private boolean mBuilt = false;
-
- /**
- * Sets the database overall schema version.
- *
- * <p>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 c5a0f63..0000000
--- 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}
- *
- * <p>Make non-backwards-compatible changes will delete all stored documents in old schema. You can
- * save your documents by setting {@link Migrator} via the {@link
- * SetSchemaRequest.Builder#setMigrator} for each type and target version you want to save.
- *
- * <p>{@link #onDowngrade} or {@link #onUpgrade} will be triggered if the version number of the
- * schema stored in AppSearch is different with the version in the request.
- *
- * <p>If any error or Exception occurred in the {@link #onDowngrade} or {@link #onUpgrade}, all the
- * setSchema request will be rejected unless the schema changes are backwards-compatible, and stored
- * documents won't have any observable changes.
- */
-public abstract class Migrator {
- /**
- * Returns {@code true} if this migrator's source type needs to be migrated to update from
- * currentVersion to finalVersion.
- *
- * <p>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}.
- *
- * <p>This method will be invoked only if the {@link SetSchemaRequest} is setting a higher
- * version number than the current {@link AppSearchSchema} saved in AppSearch.
- *
- * <p>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.
- *
- * <p>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}.
- *
- * <p>This method will be invoked only if the {@link SetSchemaRequest} is setting a lower
- * version number than the current {@link AppSearchSchema} saved in AppSearch.
- *
- * <p>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.
- *
- * <p>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 4f63bae..0000000
--- 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 3424128..0000000
--- 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<GenericDocument> mDocuments;
-
- PutDocumentsRequest(List<GenericDocument> documents) {
- mDocuments = documents;
- }
-
- /** Returns a list of {@link GenericDocument} objects that are part of this request. */
- @NonNull
- public List<GenericDocument> getGenericDocuments() {
- return Collections.unmodifiableList(mDocuments);
- }
-
- /** Builder for {@link PutDocumentsRequest} objects. */
- public static final class Builder {
- private ArrayList<GenericDocument> 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<? extends GenericDocument> 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 b86fd27..0000000
--- 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<String> mIds;
-
- RemoveByDocumentIdRequest(String namespace, Set<String> 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<String> getIds() {
- return Collections.unmodifiableSet(mIds);
- }
-
- /** Builder for {@link RemoveByDocumentIdRequest} objects. */
- public static final class Builder {
- private final String mNamespace;
- private ArraySet<String> 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<String> 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 26bdf03..0000000
--- 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.
- *
- * <p>Usage reported in this way is measured separately from usage reported via {@link
- * AppSearchSession#reportUsage}.
- *
- * <p>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).
- *
- * <p>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).
- *
- * <p>The value is in the {@link System#currentTimeMillis} time base.
- *
- * <p>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 e807803..0000000
--- 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.
- *
- * <p>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).
- *
- * <p>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).
- *
- * <p>The value is in the {@link System#currentTimeMillis} time base.
- *
- * <p>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 f6a597c..0000000
--- 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.
- *
- * <p>This allows clients to obtain:
- *
- * <ul>
- * <li>The document which matched, using {@link #getGenericDocument}
- * <li>Information about which properties in the document matched, and "snippet" information
- * containing textual summaries of the document's matches, using {@link #getMatchInfos}
- * </ul>
- *
- * <p>"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<MatchInfo> 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<MatchInfo> getMatchInfos() {
- if (mMatchInfos == null) {
- List<Bundle> 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)}.
- *
- * <p>The meaning of the ranking signal and its value is determined by the selected ranking
- * strategy:
- *
- * <ul>
- * <li>{@link SearchSpec#RANKING_STRATEGY_NONE} - this value will be 0
- * <li>{@link SearchSpec#RANKING_STRATEGY_DOCUMENT_SCORE} - the value returned by calling
- * {@link GenericDocument#getScore()} on the document returned by {@link
- * #getGenericDocument()}
- * <li>{@link SearchSpec#RANKING_STRATEGY_CREATION_TIMESTAMP} - the value returned by calling
- * {@link GenericDocument#getCreationTimestampMillis()} on the document returned by {@link
- * #getGenericDocument()}
- * <li>{@link SearchSpec#RANKING_STRATEGY_RELEVANCE_SCORE} - an arbitrary double value where a
- * higher value means more relevant
- * <li>{@link SearchSpec#RANKING_STRATEGY_USAGE_COUNT} - the number of times usage has been
- * reported for the document returned by {@link #getGenericDocument()}
- * <li>{@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()}
- * </ul>
- *
- * @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<Bundle> 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.
- *
- * <p>Class Example 1: A document contains following text in property subject:
- *
- * <p>A commonly used fake word is foo. Another nonsense word that’s used a lot is bar.
- *
- * <p>If the queryExpression is "foo".
- *
- * <p>{@link MatchInfo#getPropertyPath()} returns "subject"
- *
- * <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another
- * nonsense word that’s used a lot is bar."
- *
- * <p>{@link MatchInfo#getExactMatchRange()} returns [29, 32]
- *
- * <p>{@link MatchInfo#getExactMatch()} returns "foo"
- *
- * <p>{@link MatchInfo#getSnippetRange()} returns [26, 33]
- *
- * <p>{@link MatchInfo#getSnippet()} returns "is foo."
- *
- * <p>
- *
- * <p>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}.
- *
- * <p>Let {@code sender.name = "Test Name Jr."} and {@code sender.email =
- * "TestNameJr@gmail.com"}
- *
- * <p>If the queryExpression is "Test". We will have 2 matches.
- *
- * <p>Match-1
- *
- * <p>{@link MatchInfo#getPropertyPath()} returns "sender.name"
- *
- * <p>{@link MatchInfo#getFullText()} returns "Test Name Jr."
- *
- * <p>{@link MatchInfo#getExactMatchRange()} returns [0, 4]
- *
- * <p>{@link MatchInfo#getExactMatch()} returns "Test"
- *
- * <p>{@link MatchInfo#getSnippetRange()} returns [0, 9]
- *
- * <p>{@link MatchInfo#getSnippet()} returns "Test Name"
- *
- * <p>Match-2
- *
- * <p>{@link MatchInfo#getPropertyPath()} returns "sender.email"
- *
- * <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com"
- *
- * <p>{@link MatchInfo#getExactMatchRange()} returns [0, 20]
- *
- * <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com"
- *
- * <p>{@link MatchInfo#getSnippetRange()} returns [0, 20]
- *
- * <p>{@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.
- *
- * <p>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.
- *
- * <p>A property path is a '.' - delimited sequence of property names indicating which
- * property in the document these snippets correspond to.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>For class example 1 this returns "foo"
- */
- @NonNull
- public CharSequence getExactMatch() {
- return getSubstring(getExactMatchRange());
- }
-
- /**
- * Gets the snippet {@link MatchRange} corresponding to the given entry.
- *
- * <p>Only populated when set maxSnippetSize > 0 in {@link
- * SearchSpec.Builder#setMaxSnippetSize}.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>A property path is a dot-delimited sequence of property names indicating which
- * property in the document these snippets correspond to.
- *
- * <p>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.
- *
- * <p>All ranges are finite, and the left side of the range is always {@code <=} the right side
- * of the range.
- *
- * <p>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.
- *
- * <p>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 4853b5b..0000000
--- 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<SearchResult> 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<SearchResult> getResults() {
- if (mResults == null) {
- ArrayList<Bundle> 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 5abd4f6..0000000
--- 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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>If empty, the query will search over all schema types.
- */
- @NonNull
- public List<String> getFilterSchemas() {
- List<String> schemas = mBundle.getStringArrayList(SCHEMA_FIELD);
- if (schemas == null) {
- return Collections.emptyList();
- }
- return Collections.unmodifiableList(schemas);
- }
-
- /**
- * Returns the list of namespaces to search over.
- *
- * <p>If empty, the query will search over all namespaces.
- */
- @NonNull
- public List<String> getFilterNamespaces() {
- List<String> 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.
- *
- * <p>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<String> getFilterPackageNames() {
- List<String> 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.
- *
- * <p>If the map is empty, then all properties will be retrieved for all results.
- *
- * <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this
- * function, rather than calling it multiple times.
- */
- @NonNull
- public Map<String, List<String>> getProjections() {
- Bundle typePropertyPathsBundle = mBundle.getBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD);
- Set<String> schemas = typePropertyPathsBundle.keySet();
- Map<String, List<String>> 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<String> mSchemas = new ArrayList<>();
- private ArrayList<String> mNamespaces = new ArrayList<>();
- private ArrayList<String> 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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>If unset, the query will search over all schema types.
- */
- @NonNull
- public Builder addFilterSchemas(@NonNull Collection<String> 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.
- *
- * <p>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.
- *
- * <p>If unset, the query will search over all namespaces.
- */
- @NonNull
- public Builder addFilterNamespaces(@NonNull Collection<String> 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.
- *
- * <p>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.
- *
- * <p>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<String> packageNames) {
- Objects.requireNonNull(packageNames);
- resetIfBuilt();
- mPackageNames.addAll(packageNames);
- return this;
- }
-
- /**
- * Sets the number of results per page in the returned object.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>The list returned from {@link SearchResult#getMatchInfos} will contain at most this
- * many entries.
- *
- * <p>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.
- *
- * <p>If set to 0, snippeting is disabled and the list returned from {@link
- * SearchResult#getMatchInfos} will be empty.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>If no property paths are added for a particular type, then all properties of results
- * of that type will be retrieved.
- *
- * <p>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.
- *
- * <p>Suppose the following document is in the index.
- *
- * <pre>{@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!"
- * }
- * }</pre>
- *
- * <p>Then, suppose that a query for "important" is issued with the following projection
- * type property paths:
- *
- * <pre>{@code
- * {schema: "Email", ["subject", "sender.name", "recipients.name"]}
- * }</pre>
- *
- * <p>The above document will be returned as:
- *
- * <pre>{@code
- * Email: Document {
- * sender: Document {
- * name: "Mr. Body"
- * }
- * recipients: [
- * Document {
- * name: "John Doe"
- * }
- * Document {
- * name: "Jane Doe"
- * }
- * ]
- * subject: "IMPORTANT"
- * }
- * }</pre>
- */
- @NonNull
- public SearchSpec.Builder addProjection(
- @NonNull String schema, @NonNull Collection<String> propertyPaths) {
- Objects.requireNonNull(schema);
- Objects.requireNonNull(propertyPaths);
- resetIfBuilt();
- ArrayList<String> 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.
- *
- * <p>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 b72ca9a..0000000
--- 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.
- *
- * <p>The schema is composed of a collection of {@link AppSearchSchema} objects, each of which
- * defines a unique type of data.
- *
- * <p>The first call to SetSchemaRequest will set the provided schema and store it within the {@link
- * AppSearchSession} database.
- *
- * <p>Subsequent calls will compare the provided schema to the previously saved schema, to determine
- * how to treat existing documents.
- *
- * <p>The following types of schema modifications are always safe and are made without deleting any
- * existing documents:
- *
- * <ul>
- * <li>Addition of new {@link AppSearchSchema} types
- * <li>Addition of new properties to an existing {@link AppSearchSchema} type
- * <li>Changing the cardinality of a property to be less restrictive
- * </ul>
- *
- * <p>The following types of schema changes are not backwards compatible:
- *
- * <ul>
- * <li>Removal of an existing {@link AppSearchSchema} type
- * <li>Removal of a property from an existing {@link AppSearchSchema} type
- * <li>Changing the data type of an existing property
- * <li>Changing the cardinality of a property to be more restrictive
- * </ul>
- *
- * <p>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.
- *
- * <p>Backward incompatible changes can be made by :
- *
- * <ul>
- * <li>setting {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. This
- * deletes all documents that are incompatible with the new schema. The new schema is then
- * saved and persisted to disk.
- * <li>Add a {@link Migrator} for each incompatible type and make no deletion. The migrator will
- * migrate documents from it's old schema version to the new version. Migrated types will be
- * set into both {@link SetSchemaResponse#getIncompatibleTypes()} and {@link
- * SetSchemaResponse#getMigratedTypes()}. See the migration section below.
- * </ul>
- *
- * @see AppSearchSession#setSchema
- * @see Migrator
- */
-public final class SetSchemaRequest {
- private final Set<AppSearchSchema> mSchemas;
- private final Set<String> mSchemasNotDisplayedBySystem;
- private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages;
- private final Map<String, Migrator> mMigrators;
- private final boolean mForceOverride;
- private final int mVersion;
-
- SetSchemaRequest(
- @NonNull Set<AppSearchSchema> schemas,
- @NonNull Set<String> schemasNotDisplayedBySystem,
- @NonNull Map<String, Set<PackageIdentifier>> schemasVisibleToPackages,
- @NonNull Map<String, Migrator> 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<AppSearchSchema> 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<String> getSchemasNotDisplayedBySystem() {
- return Collections.unmodifiableSet(mSchemasNotDisplayedBySystem);
- }
-
- /**
- * Returns a mapping of schema types to the set of packages that have access to that schema
- * type.
- *
- * <p>It’s inefficient to call this method repeatedly.
- */
- @NonNull
- public Map<String, Set<PackageIdentifier>> getSchemasVisibleToPackages() {
- Map<String, Set<PackageIdentifier>> 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<String, Migrator> getMigrators() {
- return Collections.unmodifiableMap(mMigrators);
- }
-
- /**
- * Returns a mapping of {@link AppSearchSchema} types to the set of packages that have access to
- * that schema type.
- *
- * <p>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<String, Set<PackageIdentifier>> 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<AppSearchSchema> mSchemas = new ArraySet<>();
- private ArraySet<String> mSchemasNotDisplayedBySystem = new ArraySet<>();
- private ArrayMap<String, Set<PackageIdentifier>> mSchemasVisibleToPackages =
- new ArrayMap<>();
- private ArrayMap<String, Migrator> 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.
- *
- * <p>An {@link AppSearchSchema} object represents one type of structured data.
- *
- * <p>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.
- *
- * <p>An {@link AppSearchSchema} object represents one type of structured data.
- */
- @NonNull
- public Builder addSchemas(@NonNull Collection<AppSearchSchema> 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.
- *
- * <p>This setting applies to the provided {@code schemaType} only, and does not persist
- * across {@link AppSearchSession#setSchema} calls.
- *
- * <p>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.
- *
- * <p>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}.
- *
- * <p>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.
- *
- * <p>For two-way data sharing, both applications need to explicitly grant Read access to
- * one another.
- *
- * <p>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<PackageIdentifier> 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.
- *
- * <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type
- * from the current version number stored in AppSearch to the final version set via {@link
- * #setVersion}.
- *
- * <p>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}.
- *
- * <p>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.
- *
- * <p>The key of the map is the schema type that the {@link Migrator} value applies to.
- *
- * <p>The {@link Migrator} migrates all {@link GenericDocument}s under given schema type
- * from the current version number stored in AppSearch to the final version set via {@link
- * #setVersion}.
- *
- * <p>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}.
- *
- * <p>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<String, Migrator> migrators) {
- Objects.requireNonNull(migrators);
- resetIfBuilt();
- mMigrators.putAll(migrators);
- return this;
- }
-
- /**
- * Sets whether or not to override the current schema in the {@link AppSearchSession}
- * database.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>The {@link AppSearchSession} database can only ever hold documents for one version at
- * a time.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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<String> 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<String, Set<PackageIdentifier>> schemasVisibleToPackages =
- new ArrayMap<>(mSchemasVisibleToPackages.size());
- for (Map.Entry<String, Set<PackageIdentifier>> 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 a3a4a23..0000000
--- 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:
- *
- * <ul>
- * <li>{@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.
- * <li>Translate multiple {@link MigrationFailure}s to bundles in {@link Builder} and then
- * back in constructor will be a huge waste.
- * </ul>
- */
- private final List<MigrationFailure> mMigrationFailures;
-
- /** Cache of the inflated deleted schema types. Comes from inflating mBundles at first use. */
- @Nullable private Set<String> mDeletedTypes;
-
- /** Cache of the inflated migrated schema types. Comes from inflating mBundles at first use. */
- @Nullable private Set<String> mMigratedTypes;
-
- /**
- * Cache of the inflated incompatible schema types. Comes from inflating mBundles at first use.
- */
- @Nullable private Set<String> mIncompatibleTypes;
-
- SetSchemaResponse(@NonNull Bundle bundle, @NonNull List<MigrationFailure> 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}.
- *
- * <p>A {@link MigrationFailure} will be generated if the system trying to save a post-migrated
- * {@link GenericDocument} but fail.
- *
- * <p>{@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<MigrationFailure> getMigrationFailures() {
- return Collections.unmodifiableList(mMigrationFailures);
- }
-
- /**
- * Returns a {@link Set} of deleted schema types.
- *
- * <p>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.
- *
- * <p>Documents for a deleted type are removed from the database.
- */
- @NonNull
- public Set<String> 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.
- *
- * <p>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.
- *
- * <p>If a document fails to be migrated, a {@link MigrationFailure} will be generated for that
- * document.
- *
- * @see Migrator
- */
- @NonNull
- public Set<String> 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.
- *
- * <p>If a {@link Migrator} is provided for this type and the migration is success triggered.
- * The type will also appear in {@link #getMigratedTypes()}.
- *
- * @see SetSchemaRequest
- * @see AppSearchSession#setSchema
- * @see SetSchemaRequest.Builder#setForceOverride
- */
- @NonNull
- public Set<String> 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<MigrationFailure> mMigrationFailures = new ArrayList<>();
- private ArrayList<String> mDeletedTypes = new ArrayList<>();
- private ArrayList<String> mMigratedTypes = new ArrayList<>();
- private ArrayList<String> mIncompatibleTypes = new ArrayList<>();
- private boolean mBuilt = false;
-
- /** Adds {@link MigrationFailure}s to the list of migration failures. */
- @NonNull
- public Builder addMigrationFailures(
- @NonNull Collection<MigrationFailure> 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<String> 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<String> 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<String> 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<Void> 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 64d4828..0000000
--- 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.
- *
- * <p>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.
- *
- * <p>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 62593ae8..0000000
--- 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.
- *
- * <p>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 <T> AppSearchResult<T> 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 5f8da7f..0000000
--- 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 e77043f..0000000
--- 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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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 b494c3c..0000000
--- 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.
- *
- * <p>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()}.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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 f2cc3b9..0000000
--- 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.
- *
- * <p>
- *
- * <ul>
- * <li>0: no tracing.
- * <li>1: fast tracing (statuses/counts only)
- * <li>2: full tracing (complete messages)
- * </ul>
- */
- private static final int PII_TRACE_LEVEL = 0;
-
- private final String mTag;
-
- public LogUtil(@NonNull String tag) {
- mTag = Objects.requireNonNull(tag);
- }
-
- /** Returns whether piiTrace() is enabled (PII_TRACE_LEVEL > 0). */
- public boolean isPiiTraceEnabled() {
- return PII_TRACE_LEVEL > 0;
- }
-
- /**
- * If icing lib interaction tracing is enabled via {@link #PII_TRACE_LEVEL}, logs the provided
- * message to logcat.
- *
- * <p>If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately.
- */
- public void piiTrace(@NonNull String message) {
- piiTrace(message, /*fastTraceObj=*/ null, /*fullTraceObj=*/ null);
- }
-
- /**
- * If icing lib interaction tracing is enabled via {@link #PII_TRACE_LEVEL}, logs the provided
- * message and object to logcat.
- *
- * <p>If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately.
- *
- * <p>Otherwise, {@code traceObj} is logged if it is non-null.
- */
- public void piiTrace(@NonNull String message, @Nullable Object traceObj) {
- piiTrace(message, /*fastTraceObj=*/ traceObj, /*fullTraceObj=*/ null);
- }
-
- /**
- * If icing lib interaction tracing is enabled via {@link #PII_TRACE_LEVEL}, logs the provided
- * message and objects to logcat.
- *
- * <p>If {@link #PII_TRACE_LEVEL} is 0, nothing is logged and this method returns immediately.
- *
- * <p>If {@link #PII_TRACE_LEVEL} is 1, {@code fastTraceObj} is logged if it is non-null.
- *
- * <p>If {@link #PII_TRACE_LEVEL} is 2, {@code fullTraceObj} is logged if it is non-null, else
- * {@code fastTraceObj} is logged if it is non-null..
- */
- public void piiTrace(
- @NonNull String message, @Nullable Object fastTraceObj, @Nullable Object fullTraceObj) {
- if (PII_TRACE_LEVEL == 0) {
- return;
- }
- StringBuilder builder = new StringBuilder("(trace) ").append(message);
- if (PII_TRACE_LEVEL == 1 && fastTraceObj != null) {
- builder.append(": ").append(fastTraceObj);
- } else if (PII_TRACE_LEVEL == 2 && fullTraceObj != null) {
- builder.append(": ").append(fullTraceObj);
- } else if (PII_TRACE_LEVEL == 2 && fastTraceObj != null) {
- builder.append(": ").append(fastTraceObj);
- }
- Log.i(mTag, builder.toString());
- }
-}
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 10e014b..0000000
--- 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.
- *
- * <p>{@link Migrator#shouldMigrate} returns {@code true} will make the {@link Migrator} active.
- */
- @NonNull
- public static Map<String, Migrator> getActiveMigrators(
- @NonNull Set<AppSearchSchema> existingSchemas,
- @NonNull Map<String, Migrator> migrators,
- int currentVersion,
- int finalVersion) {
- if (currentVersion == finalVersion) {
- return Collections.emptyMap();
- }
- Set<String> existingTypes = new ArraySet<>(existingSchemas.size());
- for (AppSearchSchema schema : existingSchemas) {
- existingTypes.add(schema.getSchemaType());
- }
-
- Map<String, Migrator> activeMigrators = new ArrayMap<>();
- for (Map.Entry<String, Migrator> 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<String> activeMigrators)
- throws AppSearchException {
- Set<String> unmigratedIncompatibleTypes =
- new ArraySet<>(setSchemaResponse.getIncompatibleTypes());
- unmigratedIncompatibleTypes.removeAll(activeMigrators);
-
- Set<String> 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<String> deletedTypes, @NonNull Set<String> 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 b6521ff..0000000
--- 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 c79ea22..0000000
--- 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 29048b2..0000000
--- 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.
- *
- * <p>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.
- *
- * <p>Listener is registered to DeviceConfig keep the cached value up to date.
- *
- * <p>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.
- *
- * <p>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}
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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 db23a6d..0000000
--- 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<UserHandle> 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.
- *
- * <p>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<PackageInfo> installedPackageInfos = userContext
- .getPackageManager()
- .getInstalledPackages(/*flags=*/0);
- Set<String> 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<Bundle> schemaBundles,
- @NonNull List<String> schemasNotDisplayedBySystem,
- @NonNull Map<String, List<Bundle>> 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<AppSearchSchema> schemas = new ArrayList<>(schemaBundles.size());
- for (int i = 0; i < schemaBundles.size(); i++) {
- schemas.add(new AppSearchSchema(schemaBundles.get(i)));
- }
- Map<String, List<PackageIdentifier>> schemasVisibleToPackages =
- new ArrayMap<>(schemasVisibleToPackagesBundles.size());
- for (Map.Entry<String, List<Bundle>> entry :
- schemasVisibleToPackagesBundles.entrySet()) {
- List<PackageIdentifier> 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<String> 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<Bundle> 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<String, Void> 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<Void> 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<String> ids,
- @NonNull Map<String, List<String>> 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<String, Bundle> 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<Bundle> 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<Bundle> 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<String> 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<String, Void> 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<Void> 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<String, ?> 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.
- *
- * <p>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.
- *
- * <p>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<PackageInfo> 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 56e2af5..0000000
--- 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 529f2b0..0000000
--- 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<UserHandle, AppSearchUserInstance> mInstancesLocked = new ArrayMap<>();
-
- private AppSearchUserInstanceManager() {}
-
- /**
- * Gets an instance of AppSearchUserInstanceManager to be used.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>It can return an empty list if there is no {@link AppSearchUserInstance} created yet.
- */
- @NonNull
- public List<UserHandle> 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 d16168a..0000000
--- 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 d934449..0000000
--- 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 38cd7a8..0000000
--- 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 15916cc..0000000
--- 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.
- *
- * <p>Never create two instances using the same folder.
- *
- * <p>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:
- *
- * <ul>
- * <li>Rewrite SchemaType in SchemaProto by adding the package-database prefix and save into
- * SchemaTypes set in {@link #setSchema}.
- * <li>Rewrite namespace and SchemaType in DocumentProto by adding package-database prefix and
- * save to namespaces set in {@link #putDocument}.
- * <li>Remove package-database prefix when retrieving documents in {@link #getDocument} and {@link
- * #query}.
- * <li>Rewrite filters in {@link SearchSpecProto} to have all namespaces and schema types of the
- * queried database when user using empty filters in {@link #query}.
- * </ul>
- *
- * <p>Methods in this class belong to two groups, the query group and the mutate group.
- *
- * <ul>
- * <li>All methods are going to modify global parameters and data in Icing are executed under
- * WRITE lock to keep thread safety.
- * <li>All methods are going to access global parameters or query data from Icing are executed
- * under READ lock to improve query performance.
- * </ul>
- *
- * <p>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<String, Map<String, SchemaTypeConfigProto>> 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<String, Set<String>> mNamespaceMapLocked = new HashMap<>();
-
- /** Maps package name to active document count. */
- @GuardedBy("mReadWriteLock")
- private final Map<String, Integer> 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<String, Set<Long>> 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.
- *
- * <p>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.
- *
- * <p>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<SchemaTypeConfigProto> schemaProtoTypesList = schemaProto.getTypesList();
- for (int i = 0; i < schemaProtoTypesList.size(); i++) {
- SchemaTypeConfigProto schema = schemaProtoTypesList.get(i);
- String prefixedSchemaType = schema.getSchemaType();
- addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType), schema);
- }
-
- // Populate namespace map
- List<String> prefixedNamespaceList =
- getAllNamespacesResultProto.getNamespacesList();
- for (int i = 0; i < prefixedNamespaceList.size(); i++) {
- String prefixedNamespace = prefixedNamespaceList.get(i);
- addToMap(mNamespaceMapLocked, getPrefix(prefixedNamespace), prefixedNamespace);
- }
-
- // Populate document count map
- rebuildDocumentCountMapLocked(storageInfoProto);
-
- // logging prepare_schema_and_namespaces latency
- if (initStatsBuilder != null) {
- initStatsBuilder.setPrepareSchemaAndNamespacesLatencyMillis(
- (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.
- *
- * <p>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.
- *
- * <p>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<AppSearchSchema> schemas,
- @Nullable VisibilityStore visibilityStore,
- @NonNull List<String> schemasNotDisplayedBySystem,
- @NonNull Map<String, List<PackageIdentifier>> 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<String> prefixedSchemasNotDisplayedBySystem =
- new ArraySet<>(schemasNotDisplayedBySystem.size());
- for (int i = 0; i < schemasNotDisplayedBySystem.size(); i++) {
- prefixedSchemasNotDisplayedBySystem.add(
- prefix + schemasNotDisplayedBySystem.get(i));
- }
-
- Map<String, List<PackageIdentifier>> prefixedSchemasVisibleToPackages =
- new ArrayMap<>(schemasVisibleToPackages.size());
- for (Map.Entry<String, List<PackageIdentifier>> 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.
- *
- * <p>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.
- *
- * <p>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<String> 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<String> 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.
- *
- * <p>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.
- *
- * <p>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<String, List<String>> typePropertyPaths)
- throws AppSearchException {
- mReadWriteLock.readLock().lock();
- try {
- throwIfClosedLocked();
- String prefix = createPrefix(packageName, databaseName);
- List<TypePropertyMask> nonPrefixedPropertyMasks =
- TypePropertyPathToProtoConverter.toTypePropertyMaskList(typePropertyPaths);
- List<TypePropertyMask> 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<String, SchemaTypeConfigProto> 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.
- *
- * <p>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<String> 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<String> 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.
- *
- * <p>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<String> packageFilters = new ArraySet<>(searchSpec.getFilterPackageNames());
- Set<String> 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<String> prefixedSchemaFilters = new ArraySet<>();
- for (String prefix : prefixFilters) {
- List<String> 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<String> 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.
- *
- * <p>This method is inefficient to call repeatedly.
- */
- @NonNull
- public Map<String, Set<String>> getPackageToDatabases() {
- mReadWriteLock.readLock().lock();
- try {
- Map<String, Set<String>> packageToDatabases = new ArrayMap<>();
- for (String prefix : mSchemaMapLocked.keySet()) {
- String packageName = getPackageName(prefix);
-
- Set<String> 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<String> prefixes,
- @NonNull Set<String> 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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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<String> 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<String> 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<String, Set<String>> packageToDatabases = getPackageToDatabases();
- Set<String> 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<String> wantedPrefixedNamespaces = new ArraySet<>();
- for (String database : databases) {
- Set<String> 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<String, Set<String>> packageToDatabases = getPackageToDatabases();
- Set<String> 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<String> 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<String> 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<NamespaceStorageInfoProto> 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.
- *
- * <p>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.
- *
- * <p>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}
- *
- * <p>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<String> 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<String> installedPackages) throws AppSearchException {
- mReadWriteLock.writeLock().lock();
- try {
- throwIfClosedLocked();
- Map<String, Set<String>> 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<String, Set<String>> entry : packageToDatabases.entrySet()) {
- String packageName = entry.getKey();
- Set<String> 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.
- *
- * <p>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<NamespaceStorageInfoProto> namespaceStorageInfoProtoList =
- storageInfoProto.getDocumentStorageInfo().getNamespaceStorageInfoList();
- for (int i = 0; i < namespaceStorageInfoProtoList.size(); i++) {
- NamespaceStorageInfoProto namespaceStorageInfoProto =
- namespaceStorageInfoProtoList.get(i);
- String packageName = getPackageName(namespaceStorageInfoProto.getNamespace());
- Integer oldCount = mDocumentCountMapLocked.get(packageName);
- int newCount;
- if (oldCount == null) {
- newCount = namespaceStorageInfoProto.getNumAliveDocuments();
- } else {
- newCount = oldCount + namespaceStorageInfoProto.getNumAliveDocuments();
- }
- mDocumentCountMapLocked.put(packageName, newCount);
- }
- }
-
- /** Wrapper around schema changes */
- @VisibleForTesting
- static class RewrittenSchemaResults {
- // Any prefixed types that used to exist in the schema, but are deleted in the new one.
- final Set<String> mDeletedPrefixedTypes = new ArraySet<>();
-
- // Map of prefixed schema types to SchemaTypeConfigProtos that were part of the new schema.
- final Map<String, SchemaTypeConfigProto> 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<String, SchemaTypeConfigProto> 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}.
- *
- * <p>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<String> prefixes,
- @NonNull Set<String> allowedPrefixedSchemas) {
- // Create a copy since retainAll() modifies the original set.
- Set<String> 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<String> 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<String> 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.
- *
- * <p>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<String> getAllowedPrefixSchemasLocked(
- @NonNull String prefix, @NonNull SearchSpec searchSpec) {
- Set<String> allowedPrefixedSchemas = new ArraySet<>();
-
- // Add all the schema filters the client specified.
- List<String> 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<String, SchemaTypeConfigProto> prefixedSchemaMap = mSchemaMapLocked.get(prefix);
- if (prefixedSchemaMap != null) {
- allowedPrefixedSchemas.addAll(prefixedSchemaMap.keySet());
- }
- }
- return allowedPrefixedSchemas;
- }
-
- /**
- * Rewrites the typePropertyMasks that exist in {@code prefixes}.
- *
- * <p>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<String> prefixes,
- @NonNull Set<String> allowedPrefixedSchemas) {
- // Create a copy since retainAll() modifies the original set.
- Set<String> existingPrefixes = new ArraySet<>(mNamespaceMapLocked.keySet());
- existingPrefixes.retainAll(prefixes);
-
- List<TypePropertyMask> 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.
- *
- * <p>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<String> prefixes,
- int maxNumResults) {
- Set<String> 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<String, List<String>> packageAndNamespaceToNamespaces = new ArrayMap<>();
- for (String prefix : existingPrefixes) {
- Set<String> 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<String> namespaceList =
- packageAndNamespaceToNamespaces.get(emptyDatabasePrefixedNamespace);
- if (namespaceList == null) {
- namespaceList = new ArrayList<>();
- packageAndNamespaceToNamespaces.put(
- emptyDatabasePrefixedNamespace, namespaceList);
- }
- namespaceList.add(prefixedNamespace);
- }
- }
-
- for (List<String> namespaces : packageAndNamespaceToNamespaces.values()) {
- resultSpecBuilder.addResultGroupings(
- ResultSpecProto.ResultGrouping.newBuilder()
- .addAllNamespaces(namespaces)
- .setMaxResults(maxNumResults));
- }
- }
-
- /**
- * Adds result groupings for each package being queried for.
- *
- * <p>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<String> prefixes,
- int maxNumResults) {
- Set<String> existingPrefixes = new ArraySet<>(mNamespaceMapLocked.keySet());
- existingPrefixes.retainAll(prefixes);
-
- // Build up a map of package to namespaces.
- Map<String, List<String>> packageToNamespacesMap = new ArrayMap<>();
- for (String prefix : existingPrefixes) {
- Set<String> prefixedNamespaces = mNamespaceMapLocked.get(prefix);
- if (prefixedNamespaces == null) {
- continue;
- }
- String packageName = getPackageName(prefix);
- List<String> packageNamespaceList = packageToNamespacesMap.get(packageName);
- if (packageNamespaceList == null) {
- packageNamespaceList = new ArrayList<>();
- packageToNamespacesMap.put(packageName, packageNamespaceList);
- }
- packageNamespaceList.addAll(prefixedNamespaces);
- }
-
- for (List<String> prefixedNamespaces : packageToNamespacesMap.values()) {
- resultSpecBuilder.addResultGroupings(
- ResultSpecProto.ResultGrouping.newBuilder()
- .addAllNamespaces(prefixedNamespaces)
- .setMaxResults(maxNumResults));
- }
- }
-
- /**
- * Adds result groupings for each namespace being queried for.
- *
- * <p>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<String> prefixes,
- int maxNumResults) {
- Set<String> 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<String, List<String>> namespaceToPrefixedNamespaces = new ArrayMap<>();
- for (String prefix : existingPrefixes) {
- Set<String> 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<String> groupedPrefixedNamespaces =
- namespaceToPrefixedNamespaces.get(namespace);
- if (groupedPrefixedNamespaces == null) {
- groupedPrefixedNamespaces = new ArrayList<>();
- namespaceToPrefixedNamespaces.put(namespace, groupedPrefixedNamespaces);
- }
- groupedPrefixedNamespaces.add(prefixedNamespace);
- }
- }
-
- for (List<String> 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<Long> 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<Long> 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<String, Set<String>> map, String prefix, String prefixedValue) {
- Set<String> values = map.get(prefix);
- if (values == null) {
- values = new ArraySet<>();
- map.put(prefix, values);
- }
- values.add(prefixedValue);
- }
-
- private static void addToMap(
- Map<String, Map<String, SchemaTypeConfigProto>> map,
- String prefix,
- SchemaTypeConfigProto schemaTypeConfigProto) {
- Map<String, SchemaTypeConfigProto> schemaTypeMap = map.get(prefix);
- if (schemaTypeMap == null) {
- schemaTypeMap = new ArrayMap<>();
- map.put(prefix, schemaTypeMap);
- }
- schemaTypeMap.put(schemaTypeConfigProto.getSchemaType(), schemaTypeConfigProto);
- }
-
- private static void removeFromMap(
- Map<String, Map<String, SchemaTypeConfigProto>> map, String prefix, String schemaType) {
- Map<String, SchemaTypeConfigProto> 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.
- *
- * <p>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()}.
- *
- * <p>This method will trigger {@link IcingSearchEngine#getOptimizeInfo()} to check resources
- * that could be released for every {@link #CHECK_OPTIMIZE_INTERVAL} mutations.
- *
- * <p>{@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.
- *
- * <p>This method will directly trigger {@link IcingSearchEngine#getOptimizeInfo()} to check
- * resources that could be released.
- *
- * <p>{@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<String, Map<String, SchemaTypeConfigProto>> schemaMap)
- throws AppSearchException {
- // Parallel array of package names for each document search result.
- List<String> packageNames = new ArrayList<>(searchResultProto.getResultsCount());
-
- // Parallel array of database names for each document search result.
- List<String> 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.
- *
- * <p>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 98cedc7..0000000
--- 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.
- *
- * <p>Any implementation needs to provide general information on how to log all the stats types.
- * (e.g. {@link CallStats})
- *
- * <p>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 cd653e5..0000000
--- 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.
- *
- * <p>E.g. we need to have helper functions to copy numbers from IcingLib to stats classes.
- *
- * @hide
- */
-public final class AppSearchLoggerHelper {
- private AppSearchLoggerHelper() {}
-
- /**
- * Copies native 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 3f5723e..0000000
--- 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.
- *
- * <p>Enforced at the time of serializing the document into a proto.
- *
- * <p>This limit has two purposes:
- *
- * <ol>
- * <li>Prevent the system service from using too much memory during indexing or querying by
- * capping the size of the data structures it needs to buffer
- * <li>Prevent apps from using a very large amount of data by storing exceptionally large
- * documents.
- * </ol>
- */
- int getMaxDocumentSizeBytes();
-
- /**
- * The maximum number of documents a single app is allowed to index.
- *
- * <p>Enforced at indexing time.
- *
- * <p>This limit has two purposes:
- *
- * <ol>
- * <li>Protect icing lib's docid space from being overwhelmed by a single app. The overall
- * docid limit is currently 2^20 (~1 million)
- * <li>Prevent apps from using a very large amount of data on the system by storing too many
- * documents.
- * </ol>
- */
- int getMaxDocumentCount();
-}
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/OptimizeStrategy.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/OptimizeStrategy.java
deleted file mode 100644
index 6cb84bc..0000000
--- 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 0fabab0..0000000
--- 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 9ce916f..0000000
--- 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<String> 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}.
- *
- * <p>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<String, SchemaTypeConfigProto> 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 e340de0..0000000
--- 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 3e4e7d2..0000000
--- 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<AppSearchSchema.PropertyConfig> 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<PropertyConfigProto> 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 6b443b3..0000000
--- 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<String> packageNames,
- @NonNull List<String> databaseNames,
- @NonNull Map<String, Map<String, SchemaTypeConfigProto>> 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<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount());
- for (int i = 0; i < proto.getResultsCount(); i++) {
- String prefix = createPrefix(packageNames.get(i), databaseNames.get(i));
- Map<String, SchemaTypeConfigProto> 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<String, SchemaTypeConfigProto> 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 8f9e9bd..0000000
--- 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 ed73593..0000000
--- 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 acf04ef..0000000
--- 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 <code>Map<String, List<String>></code> into <code>List<TypePropertyMask></code>.
- *
- * @hide
- */
-public final class TypePropertyPathToProtoConverter {
- private TypePropertyPathToProtoConverter() {}
-
- /** Extracts {@link TypePropertyMask} information from a {@link Map}. */
- @NonNull
- public static List<TypePropertyMask> toTypePropertyMaskList(
- @NonNull Map<String, List<String>> typePropertyPaths) {
- Objects.requireNonNull(typePropertyPaths);
- List<TypePropertyMask> typePropertyMasks = new ArrayList<>(typePropertyPaths.size());
- for (Map.Entry<String, List<String>> 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 81fb418..0000000
--- 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.
- *
- * <p>This class can set which stats to log for both batch and non-batch {@link
- * android.app.appsearch.AppSearchSession} calls.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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 72befa7..0000000
--- 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.
- *
- * <p>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.
- *
- * <p>Possible recovery causes for document store:
- * <li>{@link InitializeStats#RECOVERY_CAUSE_DATA_LOSS}
- * <li>{@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
- * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
- */
- @RecoveryCause
- public int getDocumentStoreRecoveryCause() {
- return mNativeDocumentStoreRecoveryCause;
- }
-
- /**
- * Returns restoration cause for index store.
- *
- * <p>Possible causes:
- * <li>{@link InitializeStats#RECOVERY_CAUSE_INCONSISTENT_WITH_GROUND_TRUTH}
- * <li>{@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
- * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
- */
- @RecoveryCause
- public int getIndexRestorationCause() {
- return mNativeIndexRestorationCause;
- }
-
- /**
- * Returns recovery cause for schema store.
- *
- * <p>Possible causes:
- * <li>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}.
- *
- * <p>If no value has been set, the default value is {@link AppSearchResult#RESULT_OK}.
- */
- @AppSearchResult.ResultCode
- public int getResetStatusCode() {
- return mResetStatusCode;
- }
-
- InitializeStats(@NonNull Builder builder) {
- 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.
- *
- * <p>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.
- *
- * <p>Possible recovery causes for document store:
- * <li>{@link InitializeStats#RECOVERY_CAUSE_DATA_LOSS}
- * <li>{@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
- * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
- */
- @NonNull
- public Builder setDocumentStoreRecoveryCause(
- @RecoveryCause int documentStoreRecoveryCause) {
- mNativeDocumentStoreRecoveryCause = documentStoreRecoveryCause;
- return this;
- }
-
- /**
- * Sets restoration cause for index store.
- *
- * <p>Possible causes:
- * <li>{@link InitializeStats#DOCUMENT_STORE_DATA_STATUS_COMPLETE_LOSS}
- * <li>{@link InitializeStats#RECOVERY_CAUSE_TOTAL_CHECKSUM_MISMATCH}
- * <li>{@link InitializeStats#RECOVERY_CAUSE_IO_ERROR}
- */
- @NonNull
- public Builder setIndexRestorationCause(@RecoveryCause int indexRestorationCause) {
- mNativeIndexRestorationCause = indexRestorationCause;
- return this;
- }
-
- /**
- * Returns recovery cause for schema store.
- *
- * <p>Possible causes:
- * <li>{@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 83bd50f..0000000
--- 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 7ba1816..0000000
--- 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 b900c49..0000000
--- 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 6e1e2d5..0000000
--- 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.
- *
- * <p>If all schema fields are backward compatible, the schema will be successful set to Icing.
- * Otherwise, we will retrieve incompatible types here.
- *
- * <p>Please see {@link SetSchemaRequest} for what is "incompatible".
- */
- public int getFirstSetSchemaLatencyMillis() {
- return mFirstSetSchemaLatencyMillis;
- }
-
- /**
- * Returns latency of second SetSchema action in milliseconds.
- *
- * <p>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 d7904f3..0000000
--- 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 9d789a8..0000000
--- 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.
- *
- * <p>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.
- *
- * <p>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 80cfe89..0000000
--- 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 fb89250..0000000
--- 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<String> schemasNotDisplayedBySystem,
- @NonNull Map<String, List<PackageIdentifier>> 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 fdf6a00..0000000
--- 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.
- *
- * <p>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()}.
- *
- * <p> 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
- *
- * <p>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.
- *
- * <p>It maps packageName to packageUid.
- *
- * <p>The entry will be removed whenever the app gets uninstalled
- */
- @GuardedBy("mLock")
- private final Map<String, Integer> 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.
- *
- * <p>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.
- *
- * <p>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 dd56739..0000000
--- 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.
- *
- * <p>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.
- *
- * <p>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<StatsEvent> 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<StatsEvent> data) {
- AppSearchUserInstanceManager userInstanceManager =
- AppSearchUserInstanceManager.getInstance();
- List<UserHandle> 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 714ffb6..0000000
--- 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 95905af..0000000
--- 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<String, Map<String, Set<String>>> mMap = new ArrayMap<>();
-
- /**
- * Sets the prefixed schemas that are opted out of platform surfacing for the database.
- *
- * <p>Any existing mappings for this prefix are overwritten.
- */
- public void setNotDisplayedBySystem(
- @NonNull String packageName,
- @NonNull String databaseName,
- @NonNull Set<String> prefixedSchemas) {
- Map<String, Set<String>> 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<String, Set<String>> databaseToSchemaType = mMap.get(packageName);
- if (databaseToSchemaType == null) {
- // No opt-outs for this package
- return true;
- }
- Set<String> 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 b010870..0000000
--- 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.
- *
- * <p>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<VisibilityDocument.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 ce142d6..0000000
--- 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.
- *
- * <p>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.
- *
- * <p>These visibility settings are used to ensure AppSearch queries respect the clients' settings
- * on who their data is visible to.
- *
- * <p>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<String, Set<String>> 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<String, Set<PackageIdentifier>> 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<PackageIdentifier> 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<String> schemasNotDisplayedBySystem,
- @NonNull Map<String, List<PackageIdentifier>> 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<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>();
- List<VisibleToPackagesDocument> visibleToPackagesDocuments = new ArrayList<>();
- for (Map.Entry<String, List<PackageIdentifier>> 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.
- *
- * <p>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<PackageIdentifier> 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 8d50339..0000000
--- 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}.
- *
- * <p>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<VisibleToPackagesDocument.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 e2b51a7..0000000
--- 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<String, Map<String, Map<String, Set<PackageIdentifier>>>> mMap =
- new ArrayMap<>();
-
- /**
- * Sets the prefixed schemas that have package visibility in the given database.
- *
- * <p>Any existing mappings for this prefix are overwritten.
- */
- public void setVisibleToPackages(
- @NonNull String packageName,
- @NonNull String databaseName,
- @NonNull Map<String, Set<PackageIdentifier>> schemaToPackageIdentifier) {
- Map<String, Map<String, Set<PackageIdentifier>>> 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.
- *
- * <p>If no such settings exist, returns the empty set.
- */
- @NonNull
- public Set<PackageIdentifier> getAccessiblePackages(
- @NonNull String packageName,
- @NonNull String databaseName,
- @NonNull String prefixedSchema) {
- Map<String, Map<String, Set<PackageIdentifier>>> databaseToSchemaTypeToVisibility =
- mMap.get(packageName);
- if (databaseToSchemaTypeToVisibility == null) {
- return Collections.emptySet();
- }
- Map<String, Set<PackageIdentifier>> schemaTypeToVisibility =
- databaseToSchemaTypeToVisibility.get(databaseName);
- if (schemaTypeToVisibility == null) {
- return Collections.emptySet();
- }
- Set<PackageIdentifier> 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 a81d7d80..0000000
--- 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 5407cb4..0000000
--- 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 71b4f36..0000000
--- 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<AppSearchSessionShim> 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<AppSearchSessionShim> 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<AppSearchSessionShim> createSearchSession(
- @NonNull Context context,
- @NonNull AppSearchManager.SearchContext searchContext,
- @NonNull ExecutorService executor) {
- AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
- SettableFuture<AppSearchResult<AppSearchSession>> 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<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest request) {
- SettableFuture<AppSearchResult<SetSchemaResponse>> future = SettableFuture.create();
- mAppSearchSession.setSchema(request, mExecutor, mExecutor, future::set);
- return Futures.transformAsync(future, this::transformResult, mExecutor);
- }
-
- @Override
- @NonNull
- public ListenableFuture<GetSchemaResponse> getSchema() {
- SettableFuture<AppSearchResult<GetSchemaResponse>> future = SettableFuture.create();
- mAppSearchSession.getSchema(mExecutor, future::set);
- return Futures.transformAsync(future, this::transformResult, mExecutor);
- }
-
- @NonNull
- @Override
- public ListenableFuture<Set<String>> getNamespaces() {
- SettableFuture<AppSearchResult<Set<String>>> future = SettableFuture.create();
- mAppSearchSession.getNamespaces(mExecutor, future::set);
- return Futures.transformAsync(future, this::transformResult, mExecutor);
- }
-
- @Override
- @NonNull
- public ListenableFuture<AppSearchBatchResult<String, Void>> put(
- @NonNull PutDocumentsRequest request) {
- SettableFuture<AppSearchBatchResult<String, Void>> future = SettableFuture.create();
- mAppSearchSession.put(
- request, mExecutor, new BatchResultCallbackAdapter<>(future));
- return future;
- }
-
- @Override
- @NonNull
- public ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByDocumentId(
- @NonNull GetByDocumentIdRequest request) {
- SettableFuture<AppSearchBatchResult<String, GenericDocument>> 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<Void> reportUsage(@NonNull ReportUsageRequest request) {
- SettableFuture<AppSearchResult<Void>> future = SettableFuture.create();
- mAppSearchSession.reportUsage(request, mExecutor, future::set);
- return Futures.transformAsync(future, this::transformResult, mExecutor);
- }
-
- @Override
- @NonNull
- public ListenableFuture<AppSearchBatchResult<String, Void>> remove(
- @NonNull RemoveByDocumentIdRequest request) {
- SettableFuture<AppSearchBatchResult<String, Void>> future = SettableFuture.create();
- mAppSearchSession.remove(request, mExecutor, new BatchResultCallbackAdapter<>(future));
- return future;
- }
-
- @Override
- @NonNull
- public ListenableFuture<Void> remove(
- @NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
- SettableFuture<AppSearchResult<Void>> future = SettableFuture.create();
- mAppSearchSession.remove(queryExpression, searchSpec, mExecutor, future::set);
- return Futures.transformAsync(future, this::transformResult, mExecutor);
- }
-
- @NonNull
- @Override
- public ListenableFuture<StorageInfo> getStorageInfo() {
- SettableFuture<AppSearchResult<StorageInfo>> 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<Void> requestFlush() {
- SettableFuture<AppSearchResult<Void>> 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 <T> ListenableFuture<T> transformResult(
- @NonNull AppSearchResult<T> result) throws AppSearchException {
- if (!result.isSuccess()) {
- throw new AppSearchException(result.getResultCode(), result.getErrorMessage());
- }
- return Futures.immediateFuture(result.getResultValue());
- }
-
- private static final class BatchResultCallbackAdapter<K, V>
- implements BatchResultCallback<K, V> {
- private final SettableFuture<AppSearchBatchResult<K, V>> mFuture;
-
- BatchResultCallbackAdapter(SettableFuture<AppSearchBatchResult<K, V>> future) {
- mFuture = future;
- }
-
- @Override
- public void onResult(AppSearchBatchResult<K, V> 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 c35849d..0000000
--- 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<GlobalSearchSessionShim> createGlobalSearchSession() {
- return createGlobalSearchSession(ApplicationProvider.getApplicationContext());
- }
-
- /** Only for use when called from a non-instrumented context. */
- @NonNull
- public static ListenableFuture<GlobalSearchSessionShim> createGlobalSearchSession(
- @NonNull Context context) {
- AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
- SettableFuture<AppSearchResult<GlobalSearchSession>> 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<Void> reportSystemUsage(@NonNull ReportSystemUsageRequest request) {
- SettableFuture<AppSearchResult<Void>> future = SettableFuture.create();
- mGlobalSearchSession.reportSystemUsage(request, mExecutor, future::set);
- return Futures.transformAsync(future, this::transformResult, mExecutor);
- }
-
- @Override
- public void close() {
- mGlobalSearchSession.close();
- }
-
- private <T> ListenableFuture<T> transformResult(
- @NonNull AppSearchResult<T> 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 72078f8..0000000
--- 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<List<SearchResult>> getNextPage() {
- SettableFuture<AppSearchResult<List<SearchResult>>> 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 d28d4ac..0000000
--- 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.
- *
- * <p>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<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 7b74578..0000000
--- 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.
- *
- * <p>An {@link AppSearchSessionShim} instance provides access to database operations such as
- * setting a schema, adding documents, and searching.
- *
- * <p>Instances of this interface are usually obtained from a storage implementation, e.g. {@code
- * AppSearchManager.createSearchSession()} or {@code PlatformStorage.createSearchSession()}.
- *
- * <p>All implementations of this interface must be thread safe.
- *
- * @see GlobalSearchSessionShim
- */
-public interface AppSearchSessionShim extends Closeable {
-
- /**
- * Sets the schema that represents the organizational structure of data within the AppSearch
- * database.
- *
- * <p>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<SetSchemaResponse> 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<GetSchemaResponse> 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<Set<String>> getNamespaces();
-
- /**
- * Indexes documents into the {@link AppSearchSessionShim} database.
- *
- * <p>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<AppSearchBatchResult<String, Void>> 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<AppSearchBatchResult<String, GenericDocument>> getByDocumentId(
- @NonNull GetByDocumentIdRequest request);
-
- /**
- * Retrieves documents from the open {@link AppSearchSessionShim} that match a given query
- * string and type of search provided.
- *
- * <p>Query strings can be empty, contain one term with no operators, or contain multiple terms
- * and operators.
- *
- * <p>For query strings that are empty, all documents that match the {@link SearchSpec} will be
- * returned.
- *
- * <p>For query strings with a single term and no operators, documents that match the provided
- * query string and {@link SearchSpec} will be returned.
- *
- * <p>The following operators are supported:
- *
- * <ul>
- * <li>AND (implicit)
- * <p>AND is an operator that matches documents that contain <i>all</i> provided terms.
- * <p><b>NOTE:</b> 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".
- * <p>Example: "apple AND banana" matches documents that contain the terms "apple", "and",
- * "banana".
- * <p>Example: "apple banana" matches documents that contain both "apple" and "banana".
- * <p>Example: "apple banana cherry" matches documents that contain "apple", "banana", and
- * "cherry".
- * <li>OR
- * <p>OR is an operator that matches documents that contain <i>any</i> provided term.
- * <p>Example: "apple OR banana" matches documents that contain either "apple" or
- * "banana".
- * <p>Example: "apple OR banana OR cherry" matches documents that contain any of "apple",
- * "banana", or "cherry".
- * <li>Exclusion (-)
- * <p>Exclusion (-) is an operator that matches documents that <i>do not</i> contain the
- * provided term.
- * <p>Example: "-apple" matches documents that do not contain "apple".
- * <li>Grouped Terms
- * <p>For queries that require multiple operators and terms, terms can be grouped into
- * subqueries. Subqueries are contained within an open "(" and close ")" parenthesis.
- * <p>Example: "(donut OR bagel) (coffee OR tea)" matches documents that contain either
- * "donut" or "bagel" and either "coffee" or "tea".
- * <li>Property Restricts
- * <p>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.
- * <p>Example: "subject:important" matches documents that contain the term "important" in
- * the "subject" property.
- * </ul>
- *
- * <p>Additional search specifications, such as filtering by {@link AppSearchSchema} type or
- * adding projection, can be set by calling the corresponding {@link SearchSpec.Builder} setter.
- *
- * <p>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.
- *
- * <p>A usage report represents an event in which a user interacted with or viewed a document.
- *
- * <p>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.
- *
- * <p>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<Void> reportUsage(@NonNull ReportUsageRequest request);
-
- /**
- * Removes {@link GenericDocument} objects by document IDs in a namespace from the {@link
- * AppSearchSessionShim} database.
- *
- * <p>Removed documents will no longer be surfaced by {@link #search} or {@link
- * #getByDocumentId} calls.
- *
- * <p>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<AppSearchBatchResult<String, Void>> 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}.
- *
- * <p>An empty {@code queryExpression} matches all documents.
- *
- * <p>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<Void> remove(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
-
- /**
- * Gets the storage info for this {@link AppSearchSessionShim} database.
- *
- * <p>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<StorageInfo> getStorageInfo();
-
- /**
- * Flush all schema and document updates, additions, and deletes to disk if possible.
- *
- * <p>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<Void> 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 ec9a42ea..0000000
--- 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 <K, V> AppSearchBatchResult<K, V> checkIsBatchResultSuccess(
- Future<AppSearchBatchResult<K, V>> future) throws Exception {
- AppSearchBatchResult<K, V> result = future.get();
- assertWithMessage("AppSearchBatchResult not successful: " + result)
- .that(result.isSuccess())
- .isTrue();
- return result;
- }
-
- public static List<GenericDocument> doGet(
- AppSearchSessionShim session, String namespace, String... ids) throws Exception {
- AppSearchBatchResult<String, GenericDocument> result =
- checkIsBatchResultSuccess(
- session.getByDocumentId(
- new GetByDocumentIdRequest.Builder(namespace).addIds(ids).build()));
- assertThat(result.getSuccesses()).hasSize(ids.length);
- assertThat(result.getFailures()).isEmpty();
- List<GenericDocument> list = new ArrayList<>(ids.length);
- for (String id : ids) {
- list.add(result.getSuccesses().get(id));
- }
- return list;
- }
-
- public static List<GenericDocument> doGet(
- AppSearchSessionShim session, GetByDocumentIdRequest request) throws Exception {
- AppSearchBatchResult<String, GenericDocument> result =
- checkIsBatchResultSuccess(session.getByDocumentId(request));
- Set<String> ids = request.getIds();
- assertThat(result.getSuccesses()).hasSize(ids.size());
- assertThat(result.getFailures()).isEmpty();
- List<GenericDocument> list = new ArrayList<>(ids.size());
- for (String id : ids) {
- list.add(result.getSuccesses().get(id));
- }
- return list;
- }
-
- public static List<GenericDocument> convertSearchResultsToDocuments(
- SearchResultsShim searchResults) throws Exception {
- List<SearchResult> results = retrieveAllSearchResults(searchResults);
- List<GenericDocument> documents = new ArrayList<>(results.size());
- for (SearchResult result : results) {
- documents.add(result.getGenericDocument());
- }
- return documents;
- }
-
- public static List<SearchResult> retrieveAllSearchResults(SearchResultsShim searchResults)
- throws Exception {
- List<SearchResult> page = searchResults.getNextPage().get();
- List<SearchResult> 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 892f84e..0000000
--- 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.
- *
- * <p>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.
- *
- * <p>Applications can be granted access to documents by specifying {@link
- * SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage}, or {@link
- * SetSchemaRequest.Builder#setDocumentClassVisibilityForPackage} when building a schema.
- *
- * <p>Document access can also be granted to system UIs by specifying {@link
- * SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem}, or {@link
- * SetSchemaRequest.Builder#setDocumentClassDisplayedBySystem} when building a schema.
- *
- * <p>See {@link AppSearchSessionShim#search} for a detailed explanation on forming a query
- * string.
- *
- * <p>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.
- *
- * <p>See {@link AppSearchSessionShim#reportUsage} for a general description of document usage,
- * as well as an API that can be used by the app itself.
- *
- * <p>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<Void> 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 38f61f8..0000000
--- 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.
- *
- * <p>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}.
- *
- * <p>To fetch a page of results, call {@link #getNextPage()}.
- *
- * <p>All instances of {@link SearchResultsShim} must call {@link SearchResultsShim#close()} after
- * the results are fetched.
- *
- * <p>This class is not thread safe.
- */
-public interface SearchResultsShim extends Closeable {
- /**
- * Retrieves the next page of {@link SearchResult} objects.
- *
- * <p>The page size is configured by {@link SearchSpec.Builder#setResultCountPerPage}.
- *
- * <p>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<List<SearchResult>> 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 5c919b4..0000000
--- 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/api/current.txt b/core/api/current.txt
index 49a8cb8..e2167c7 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -31444,12 +31444,15 @@
method public int readInt();
method public void readIntArray(@NonNull int[]);
method public void readList(@NonNull java.util.List, @Nullable ClassLoader);
+ method public <T> void readList(@NonNull java.util.List<? super T>, @Nullable ClassLoader, @NonNull Class<T>);
method public long readLong();
method public void readLongArray(@NonNull long[]);
method public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
+ method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
method @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
method @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
+ method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
method @Nullable public android.os.PersistableBundle readPersistableBundle();
method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
@@ -31588,7 +31591,7 @@
public interface Parcelable {
method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int CONTENTS_FILE_DESCRIPTOR = 1; // 0x1
field public static final int PARCELABLE_WRITE_RETURN_VALUE = 1; // 0x1
}
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index f72c3a7..b574d04 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -42,7 +42,7 @@
}
public static interface PendingIntent.CancelListener {
- method public void onCancelled(@NonNull android.app.PendingIntent);
+ method public void onCanceled(@NonNull android.app.PendingIntent);
}
public class StatusBarManager {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6a57c24..ba7903b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1981,8 +1981,10 @@
public final class BluetoothDevice implements android.os.Parcelable {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean canBondWithoutDialog();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean cancelBondProcess();
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public int connect();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean createBond(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean createBondOutOfBand(int, @Nullable android.bluetooth.OobData, @Nullable android.bluetooth.OobData);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int disconnect();
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean fetchUuidsWithSdp(int);
method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public byte[] getMetadata(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getSimAccessPermission();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 9505aad..6ccdf91 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -321,7 +321,7 @@
}
public static interface PendingIntent.CancelListener {
- method public void onCancelled(@NonNull android.app.PendingIntent);
+ method public void onCanceled(@NonNull android.app.PendingIntent);
}
public final class PictureInPictureParams implements android.os.Parcelable {
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 9b9eed4..2c0f983 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -1081,12 +1081,12 @@
public void registerCancelListener(@NonNull CancelListener cancelListener) {
if (!addCancelListener(Runnable::run, cancelListener)) {
// Call the callback right away synchronously, if the PI has been canceled already.
- cancelListener.onCancelled(this);
+ cancelListener.onCanceled(this);
}
}
/**
- * Register a listener to when this pendingIntent is cancelled.
+ * Register a listener to when this pendingIntent is canceled.
*
* @return true if the listener has been set successfully. false if the {@link PendingIntent}
* has already been canceled.
@@ -1138,7 +1138,7 @@
int size = cancelListeners.size();
for (int i = 0; i < size; i++) {
final Pair<Executor, CancelListener> pair = cancelListeners.valueAt(i);
- pair.first.execute(() -> pair.second.onCancelled(this));
+ pair.first.execute(() -> pair.second.onCanceled(this));
}
}
@@ -1152,7 +1152,7 @@
}
/**
- * Un-register a listener to when this pendingIntent is cancelled.
+ * Un-register a listener to when this pendingIntent is canceled.
*
* @hide
*/
@@ -1462,7 +1462,7 @@
}
/**
- * A listener to when a pending intent is cancelled
+ * A listener to when a pending intent is canceled
*
* @hide
*/
@@ -1470,11 +1470,11 @@
@TestApi
public interface CancelListener {
/**
- * Called when a Pending Intent is cancelled.
+ * Called when a Pending Intent is canceled.
*
- * @param intent The intent that was cancelled.
+ * @param intent The intent that was canceled.
*/
- void onCancelled(@NonNull PendingIntent intent);
+ void onCanceled(@NonNull PendingIntent intent);
}
private PendingIntentInfo getCachedInfo() {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 77abbe9..e9be0ec 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2005,71 +2005,6 @@
}
/**
- * Connects all enabled and supported bluetooth profiles between the local and remote device.
- * Connection is asynchronous and you should listen to each profile's broadcast intent
- * ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful. For example,
- * to verify a2dp is connected, you would listen for
- * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
- *
- * @param device is the remote device with which to connect these profiles
- * @return true if message sent to try to connect all profiles, false if an error occurred
- *
- * @hide
- */
- @RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = {
- android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED,
- android.Manifest.permission.MODIFY_PHONE_STATE,
- })
- public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) {
- try {
- mServiceLock.readLock().lock();
- if (mService != null) {
- return mService.connectAllEnabledProfiles(device, mAttributionSource);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- } finally {
- mServiceLock.readLock().unlock();
- }
-
- return false;
- }
-
- /**
- * Disconnects all enabled and supported bluetooth profiles between the local and remote device.
- * Disconnection is asynchronous and you should listen to each profile's broadcast intent
- * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example,
- * to verify a2dp is disconnected, you would listen for
- * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
- *
- * @param device is the remote device with which to disconnect these profiles
- * @return true if message sent to try to disconnect all profiles, false if an error occurred
- *
- * @hide
- */
- @RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = {
- android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.BLUETOOTH_PRIVILEGED,
- })
- public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) {
- try {
- mServiceLock.readLock().lock();
- if (mService != null) {
- return mService.disconnectAllEnabledProfiles(device, mAttributionSource);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "", e);
- } finally {
- mServiceLock.readLock().unlock();
- }
-
- return false;
- }
-
- /**
* Return true if the multi advertisement is supported by the chipset
*
* @return true if Multiple Advertisement feature is supported
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 437d9ff..c71fcc6 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1680,6 +1680,90 @@
return false;
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value = {
+ BluetoothStatusCodes.SUCCESS,
+ BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
+ BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
+ BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
+ BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED
+ })
+ public @interface ConnectionReturnValues{}
+
+ /**
+ * Connects all user enabled and supported bluetooth profiles between the local and remote
+ * device. If no profiles are user enabled (e.g. first connection), we connect all supported
+ * profiles. If the device is not already connected, this will page the device before initiating
+ * profile connections. Connection is asynchronous and you should listen to each profile's
+ * broadcast intent ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful.
+ * For example, to verify a2dp is connected, you would listen for
+ * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
+ *
+ * @return whether the messages were successfully sent to try to connect all profiles
+ * @throws IllegalArgumentException if the device address is invalid
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
+ public @ConnectionReturnValues int connect() {
+ if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) {
+ throw new IllegalArgumentException("device cannot have an invalid address");
+ }
+
+ try {
+ if (sService == null) {
+ Log.e(TAG, "BT not enabled. Cannot connect to remote device.");
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ return sService.connectAllEnabledProfiles(this, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Disconnects all connected bluetooth profiles between the local and remote device.
+ * Disconnection is asynchronous and you should listen to each profile's broadcast intent
+ * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example,
+ * to verify a2dp is disconnected, you would listen for
+ * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
+ *
+ * @return whether the messages were successfully sent to try to disconnect all profiles
+ * @throws IllegalArgumentException if the device address is invalid
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public @ConnectionReturnValues int disconnect() {
+ if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) {
+ throw new IllegalArgumentException("device cannot have an invalid address");
+ }
+
+ try {
+ if (sService == null) {
+ Log.e(TAG, "BT not enabled. Cannot disconnect from remote device.");
+ return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+ }
+ return sService.disconnectAllEnabledProfiles(this, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Returns whether there is an open connection to this device.
*
diff --git a/core/java/android/bluetooth/BluetoothStatusCodes.java b/core/java/android/bluetooth/BluetoothStatusCodes.java
index 31bb0f6..3e46c49 100644
--- a/core/java/android/bluetooth/BluetoothStatusCodes.java
+++ b/core/java/android/bluetooth/BluetoothStatusCodes.java
@@ -21,7 +21,7 @@
/**
* A class with constants representing possible return values for Bluetooth APIs. General return
* values occupy the range 0 to 99. Profile-specific return values occupy the range 100-999.
- * API-specific return values start at 1000. The exception to this is the "other" error code which
+ * API-specific return values start at 1000. The exception to this is the "UNKNOWN" error code which
* occupies the max integer value.
*/
public final class BluetoothStatusCodes {
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index fb21ce3..7ce8d72 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -431,7 +431,7 @@
*
* @hide
*/
- public static boolean kindofEquals(BaseBundle a, BaseBundle b) {
+ public static boolean kindofEquals(@Nullable BaseBundle a, @Nullable BaseBundle b) {
return (a == b) || (a != null && a.kindofEquals(b));
}
@@ -1048,7 +1048,7 @@
*/
char getChar(String key, char defaultValue) {
unparcel();
- Object o = getValue(key);
+ Object o = mMap.get(key);
if (o == null) {
return defaultValue;
}
@@ -1451,7 +1451,7 @@
@Nullable
short[] getShortArray(@Nullable String key) {
unparcel();
- Object o = getValue(key);
+ Object o = mMap.get(key);
if (o == null) {
return null;
}
@@ -1474,7 +1474,7 @@
@Nullable
char[] getCharArray(@Nullable String key) {
unparcel();
- Object o = getValue(key);
+ Object o = mMap.get(key);
if (o == null) {
return null;
}
@@ -1543,7 +1543,7 @@
@Nullable
float[] getFloatArray(@Nullable String key) {
unparcel();
- Object o = getValue(key);
+ Object o = mMap.get(key);
if (o == null) {
return null;
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 2be9533..bd36772 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -285,6 +285,10 @@
private static final int VAL_SIZE = 26;
private static final int VAL_SIZEF = 27;
private static final int VAL_DOUBLEARRAY = 28;
+ private static final int VAL_CHAR = 29;
+ private static final int VAL_SHORTARRAY = 30;
+ private static final int VAL_CHARARRAY = 31;
+ private static final int VAL_FLOATARRAY = 32;
// The initial int32 in a Binder call's reply Parcel header:
// Keep these in sync with libbinder's binder/Status.h.
@@ -476,6 +480,23 @@
}
/**
+ * Retrieve a new Parcel object from the pool for use with a specific binder.
+ *
+ * Associate this parcel with a binder object. This marks the parcel as being prepared for a
+ * transaction on this specific binder object. Based on this, the format of the wire binder
+ * protocol may change. For future compatibility, it is recommended to use this for all
+ * Parcels.
+ *
+ * @hide
+ */
+ @NonNull
+ public static Parcel obtain(@NonNull IBinder binder) {
+ Parcel parcel = Parcel.obtain();
+ parcel.markForBinder(binder);
+ return parcel;
+ }
+
+ /**
* Put a Parcel object back into the pool. You must not touch
* the object after this call.
*/
@@ -549,16 +570,9 @@
}
/**
- * Associate this parcel with a binder object. This marks the parcel as being prepared for a
- * transaction on this specific binder object. Based on this, the format of the wire binder
- * protocol may change. This should be called before any data is written to the parcel. If this
- * is called multiple times, this will only be marked for the last binder. For future
- * compatibility, it is recommended to call this on all parcels which are being sent over
- * binder.
- *
* @hide
*/
- public void markForBinder(@NonNull IBinder binder) {
+ private void markForBinder(@NonNull IBinder binder) {
nativeMarkForBinder(mNativePtr, binder);
}
@@ -1346,6 +1360,46 @@
}
}
+ /** @hide */
+ public void writeShortArray(@Nullable short[] val) {
+ if (val != null) {
+ int n = val.length;
+ writeInt(n);
+ for (int i = 0; i < n; i++) {
+ writeInt(val[i]);
+ }
+ } else {
+ writeInt(-1);
+ }
+ }
+
+ /** @hide */
+ @Nullable
+ public short[] createShortArray() {
+ int n = readInt();
+ if (n >= 0 && n <= (dataAvail() >> 2)) {
+ short[] val = new short[n];
+ for (int i = 0; i < n; i++) {
+ val[i] = (short) readInt();
+ }
+ return val;
+ } else {
+ return null;
+ }
+ }
+
+ /** @hide */
+ public void readShortArray(@NonNull short[] val) {
+ int n = readInt();
+ if (n == val.length) {
+ for (int i = 0; i < n; i++) {
+ val[i] = (short) readInt();
+ }
+ } else {
+ throw new RuntimeException("bad array lengths");
+ }
+ }
+
public final void writeCharArray(@Nullable char[] val) {
if (val != null) {
int N = val.length;
@@ -2011,6 +2065,14 @@
return VAL_SIZE;
} else if (v instanceof double[]) {
return VAL_DOUBLEARRAY;
+ } else if (v instanceof Character) {
+ return VAL_CHAR;
+ } else if (v instanceof short[]) {
+ return VAL_SHORTARRAY;
+ } else if (v instanceof char[]) {
+ return VAL_CHARARRAY;
+ } else if (v instanceof float[]) {
+ return VAL_FLOATARRAY;
} else {
Class<?> clazz = v.getClass();
if (clazz.isArray() && clazz.getComponentType() == Object.class) {
@@ -2113,6 +2175,18 @@
case VAL_DOUBLEARRAY:
writeDoubleArray((double[]) v);
break;
+ case VAL_CHAR:
+ writeInt((Character) v);
+ break;
+ case VAL_SHORTARRAY:
+ writeShortArray((short[]) v);
+ break;
+ case VAL_CHARARRAY:
+ writeCharArray((char[]) v);
+ break;
+ case VAL_FLOATARRAY:
+ writeFloatArray((float[]) v);
+ break;
case VAL_OBJECTARRAY:
writeArray((Object[]) v);
break;
@@ -2797,7 +2871,20 @@
*/
public final void readList(@NonNull List outVal, @Nullable ClassLoader loader) {
int N = readInt();
- readListInternal(outVal, N, loader);
+ readListInternal(outVal, N, loader, /* clazz */ null);
+ }
+
+ /**
+ * Same as {@link #readList(List, ClassLoader)} but accepts {@code clazz} parameter as
+ * the type required for each item. If the item to be deserialized is not an instance
+ * of that class or any of its children class
+ * a {@link BadParcelableException} will be thrown.
+ */
+ public <T> void readList(@NonNull List<? super T> outVal,
+ @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ Objects.requireNonNull(clazz);
+ int n = readInt();
+ readListInternal(outVal, n, loader, clazz);
}
/**
@@ -2996,7 +3083,7 @@
return null;
}
ArrayList l = new ArrayList(N);
- readListInternal(l, N, loader);
+ readListInternal(l, N, loader, /* clazz */ null);
return l;
}
@@ -3396,20 +3483,29 @@
*/
@Nullable
public final Object readValue(@Nullable ClassLoader loader) {
+ return readValue(loader, /* clazz */ null);
+ }
+
+
+ /**
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
+ */
+ @Nullable
+ private <T> T readValue(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
int type = readInt();
- final Object object;
+ final T object;
if (isLengthPrefixed(type)) {
int length = readInt();
int start = dataPosition();
- object = readValue(type, loader);
+ object = readValue(type, loader, clazz);
int actual = dataPosition() - start;
if (actual != length) {
- Log.w(TAG,
+ Slog.wtfStack(TAG,
"Unparcelling of " + object + " of type " + Parcel.valueTypeToString(type)
+ " consumed " + actual + " bytes, but " + length + " expected.");
}
} else {
- object = readValue(type, loader);
+ object = readValue(type, loader, clazz);
}
return object;
}
@@ -3446,7 +3542,7 @@
setDataPosition(MathUtils.addOrThrow(dataPosition(), length));
return new LazyValue(this, start, length, type, loader);
} else {
- return readValue(type, loader);
+ return readValue(type, loader, /* clazz */ null);
}
}
@@ -3578,114 +3674,175 @@
/**
* Reads a value from the parcel of type {@code type}. Does NOT read the int representing the
* type first.
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
*/
+ @SuppressWarnings("unchecked")
@Nullable
- private Object readValue(int type, @Nullable ClassLoader loader) {
+ private <T> T readValue(int type, @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+ final Object object;
switch (type) {
- case VAL_NULL:
- return null;
+ case VAL_NULL:
+ object = null;
+ break;
- case VAL_STRING:
- return readString();
+ case VAL_STRING:
+ object = readString();
+ break;
- case VAL_INTEGER:
- return readInt();
+ case VAL_INTEGER:
+ object = readInt();
+ break;
- case VAL_MAP:
- return readHashMap(loader);
+ case VAL_MAP:
+ object = readHashMap(loader);
+ break;
- case VAL_PARCELABLE:
- return readParcelable(loader);
+ case VAL_PARCELABLE:
+ object = readParcelableInternal(loader, clazz);
+ break;
- case VAL_SHORT:
- return (short) readInt();
+ case VAL_SHORT:
+ object = (short) readInt();
+ break;
- case VAL_LONG:
- return readLong();
+ case VAL_LONG:
+ object = readLong();
+ break;
- case VAL_FLOAT:
- return readFloat();
+ case VAL_FLOAT:
+ object = readFloat();
+ break;
- case VAL_DOUBLE:
- return readDouble();
+ case VAL_DOUBLE:
+ object = readDouble();
+ break;
- case VAL_BOOLEAN:
- return readInt() == 1;
+ case VAL_BOOLEAN:
+ object = readInt() == 1;
+ break;
- case VAL_CHARSEQUENCE:
- return readCharSequence();
+ case VAL_CHARSEQUENCE:
+ object = readCharSequence();
+ break;
- case VAL_LIST:
- return readArrayList(loader);
+ case VAL_LIST:
+ object = readArrayList(loader);
+ break;
- case VAL_BOOLEANARRAY:
- return createBooleanArray();
+ case VAL_BOOLEANARRAY:
+ object = createBooleanArray();
+ break;
- case VAL_BYTEARRAY:
- return createByteArray();
+ case VAL_BYTEARRAY:
+ object = createByteArray();
+ break;
- case VAL_STRINGARRAY:
- return readStringArray();
+ case VAL_STRINGARRAY:
+ object = readStringArray();
+ break;
- case VAL_CHARSEQUENCEARRAY:
- return readCharSequenceArray();
+ case VAL_CHARSEQUENCEARRAY:
+ object = readCharSequenceArray();
+ break;
- case VAL_IBINDER:
- return readStrongBinder();
+ case VAL_IBINDER:
+ object = readStrongBinder();
+ break;
- case VAL_OBJECTARRAY:
- return readArray(loader);
+ case VAL_OBJECTARRAY:
+ object = readArray(loader);
+ break;
- case VAL_INTARRAY:
- return createIntArray();
+ case VAL_INTARRAY:
+ object = createIntArray();
+ break;
- case VAL_LONGARRAY:
- return createLongArray();
+ case VAL_LONGARRAY:
+ object = createLongArray();
+ break;
- case VAL_BYTE:
- return readByte();
+ case VAL_BYTE:
+ object = readByte();
+ break;
- case VAL_SERIALIZABLE:
- return readSerializable(loader);
+ case VAL_SERIALIZABLE:
+ object = readSerializable(loader);
+ break;
- case VAL_PARCELABLEARRAY:
- return readParcelableArray(loader);
+ case VAL_PARCELABLEARRAY:
+ object = readParcelableArray(loader);
+ break;
- case VAL_SPARSEARRAY:
- return readSparseArray(loader);
+ case VAL_SPARSEARRAY:
+ object = readSparseArray(loader);
+ break;
- case VAL_SPARSEBOOLEANARRAY:
- return readSparseBooleanArray();
+ case VAL_SPARSEBOOLEANARRAY:
+ object = readSparseBooleanArray();
+ break;
- case VAL_BUNDLE:
- return readBundle(loader); // loading will be deferred
+ case VAL_BUNDLE:
+ object = readBundle(loader); // loading will be deferred
+ break;
- case VAL_PERSISTABLEBUNDLE:
- return readPersistableBundle(loader);
+ case VAL_PERSISTABLEBUNDLE:
+ object = readPersistableBundle(loader);
+ break;
- case VAL_SIZE:
- return readSize();
+ case VAL_SIZE:
+ object = readSize();
+ break;
- case VAL_SIZEF:
- return readSizeF();
+ case VAL_SIZEF:
+ object = readSizeF();
+ break;
- case VAL_DOUBLEARRAY:
- return createDoubleArray();
+ case VAL_DOUBLEARRAY:
+ object = createDoubleArray();
+ break;
- default:
- int off = dataPosition() - 4;
- throw new RuntimeException(
- "Parcel " + this + ": Unmarshalling unknown type code " + type + " at offset " + off);
+ case VAL_CHAR:
+ object = (char) readInt();
+ break;
+
+ case VAL_SHORTARRAY:
+ object = createShortArray();
+ break;
+
+ case VAL_CHARARRAY:
+ object = createCharArray();
+ break;
+
+ case VAL_FLOATARRAY:
+ object = createFloatArray();
+ break;
+
+ default:
+ int off = dataPosition() - 4;
+ throw new RuntimeException(
+ "Parcel " + this + ": Unmarshalling unknown type code " + type
+ + " at offset " + off);
}
+ if (clazz != null && !clazz.isInstance(object)) {
+ throw new BadParcelableException("Unparcelled object " + object
+ + " is not an instance of required class " + clazz.getName()
+ + " provided in the parameter");
+ }
+ return (T) object;
}
private boolean isLengthPrefixed(int type) {
+ // In general, we want custom types and containers of custom types to be length-prefixed,
+ // this allows clients (eg. Bundle) to skip their content during deserialization. The
+ // exception to this is Bundle, since Bundle is already length-prefixed and already copies
+ // the correspondent section of the parcel internally.
switch (type) {
+ case VAL_MAP:
case VAL_PARCELABLE:
- case VAL_PARCELABLEARRAY:
case VAL_LIST:
case VAL_SPARSEARRAY:
- case VAL_BUNDLE:
+ case VAL_PARCELABLEARRAY:
+ case VAL_OBJECTARRAY:
case VAL_SERIALIZABLE:
return true;
default:
@@ -3704,17 +3861,42 @@
* @throws BadParcelableException Throws BadParcelableException if there
* was an error trying to instantiate the Parcelable.
*/
- @SuppressWarnings("unchecked")
@Nullable
public final <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader) {
- Parcelable.Creator<?> creator = readParcelableCreator(loader);
+ return readParcelableInternal(loader, /* clazz */ null);
+ }
+
+ /**
+ * Same as {@link #readParcelable(ClassLoader)} but accepts {@code clazz} parameter as the type
+ * required for each item. If the item to be deserialized is not an instance of that class or
+ * any of its children classes a {@link BadParcelableException} will be thrown.
+ */
+ @Nullable
+ public <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader,
+ @NonNull Class<T> clazz) {
+ Objects.requireNonNull(clazz);
+ return readParcelableInternal(loader, clazz);
+ }
+
+ /**
+ *
+ * @param clazz The type of the parcelable expected or {@code null} for performing no checks.
+ */
+ @SuppressWarnings("unchecked")
+ @Nullable
+ private <T> T readParcelableInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+ if (clazz != null && !Parcelable.class.isAssignableFrom(clazz)) {
+ throw new BadParcelableException("About to unparcel a parcelable object "
+ + " but class required " + clazz.getName() + " is not Parcelable");
+ }
+ Parcelable.Creator<?> creator = readParcelableCreatorInternal(loader, clazz);
if (creator == null) {
return null;
}
if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
- Parcelable.ClassLoaderCreator<?> classLoaderCreator =
- (Parcelable.ClassLoaderCreator<?>) creator;
- return (T) classLoaderCreator.createFromParcel(this, loader);
+ Parcelable.ClassLoaderCreator<?> classLoaderCreator =
+ (Parcelable.ClassLoaderCreator<?>) creator;
+ return (T) classLoaderCreator.createFromParcel(this, loader);
}
return (T) creator.createFromParcel(this);
}
@@ -3748,6 +3930,28 @@
*/
@Nullable
public final Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader loader) {
+ return readParcelableCreatorInternal(loader, /* clazz */ null);
+ }
+
+ /**
+ * Same as {@link #readParcelableCreator(ClassLoader)} but accepts {@code clazz} parameter
+ * as the required type. If the item to be deserialized is not an instance of that class
+ * or any of its children classes a {@link BadParcelableException} will be thrown.
+ */
+ @Nullable
+ public <T> Parcelable.Creator<T> readParcelableCreator(
+ @Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+ Objects.requireNonNull(clazz);
+ return readParcelableCreatorInternal(loader, clazz);
+ }
+
+ /**
+ * @param clazz The type of the parcelable expected or {@code null} for performing no checks.
+ */
+ @SuppressWarnings("unchecked")
+ @Nullable
+ private <T> Parcelable.Creator<T> readParcelableCreatorInternal(
+ @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
String name = readString();
if (name == null) {
return null;
@@ -3763,7 +3967,15 @@
creator = map.get(name);
}
if (creator != null) {
- return creator;
+ if (clazz != null) {
+ Class<?> parcelableClass = creator.getClass().getEnclosingClass();
+ if (!clazz.isAssignableFrom(parcelableClass)) {
+ throw new BadParcelableException("Parcelable creator " + name + " is not "
+ + "a subclass of required class " + clazz.getName()
+ + " provided in the parameter");
+ }
+ }
+ return (Parcelable.Creator<T>) creator;
}
try {
@@ -3779,6 +3991,14 @@
throw new BadParcelableException("Parcelable protocol requires subclassing "
+ "from Parcelable on class " + name);
}
+ if (clazz != null) {
+ if (!clazz.isAssignableFrom(parcelableClass)) {
+ throw new BadParcelableException("Parcelable creator " + name + " is not "
+ + "a subclass of required class " + clazz.getName()
+ + " provided in the parameter");
+ }
+ }
+
Field f = parcelableClass.getField("CREATOR");
if ((f.getModifiers() & Modifier.STATIC) == 0) {
throw new BadParcelableException("Parcelable protocol requires "
@@ -3816,7 +4036,7 @@
map.put(name, creator);
}
- return creator;
+ return (Parcelable.Creator<T>) creator;
}
/**
@@ -4066,13 +4286,21 @@
return result;
}
- private void readListInternal(@NonNull List outVal, int N,
+ private void readListInternal(@NonNull List outVal, int n,
@Nullable ClassLoader loader) {
- while (N > 0) {
- Object value = readValue(loader);
+ readListInternal(outVal, n, loader, null);
+ }
+
+ /**
+ * @param clazz The type of the object expected or {@code null} for performing no checks.
+ */
+ private <T> void readListInternal(@NonNull List<? super T> outVal, int n,
+ @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+ while (n > 0) {
+ T value = readValue(loader, clazz);
//Log.d(TAG, "Unmarshalling value=" + value);
outVal.add(value);
- N--;
+ n--;
}
}
@@ -4151,6 +4379,10 @@
case VAL_SIZE: return "VAL_SIZE";
case VAL_SIZEF: return "VAL_SIZEF";
case VAL_DOUBLEARRAY: return "VAL_DOUBLEARRAY";
+ case VAL_CHAR: return "VAL_CHAR";
+ case VAL_SHORTARRAY: return "VAL_SHORTARRAY";
+ case VAL_CHARARRAY: return "VAL_CHARARRAY";
+ case VAL_FLOATARRAY: return "VAL_FLOATARRAY";
case VAL_OBJECTARRAY: return "VAL_OBJECTARRAY";
case VAL_SERIALIZABLE: return "VAL_SERIALIZABLE";
default: return "UNKNOWN(" + type + ")";
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index a537c98..a396211 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -16,6 +16,7 @@
package android.os;
+import android.annotation.NonNull;
import android.annotation.IntDef;
import android.annotation.SystemApi;
@@ -202,7 +203,7 @@
* @param flags Additional flags about how the object should be written.
* May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
*/
- public void writeToParcel(Parcel dest, @WriteFlags int flags);
+ public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags);
/**
* Interface that must be implemented and provided as a public CREATOR
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 4edff27..0b50192 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -646,7 +646,7 @@
e.fillInStackTrace();
Log.w(TAG, "New hash " + hash
+ " is before end of array hash " + mHashes[index-1]
- + " at index " + index + " key " + key, e);
+ + " at index " + index + (DEBUG ? " key " + key : ""), e);
put(key, value);
return;
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index db43b5b..aa05b13 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3188,6 +3188,11 @@
and one pSIM) -->
<integer name="config_num_physical_slots">1</integer>
+ <!-- When a radio power off request is received, we will delay completing the request until
+ either IMS moves to the deregistered state or the timeout defined by this configuration
+ elapses. If 0, this feature is disabled and we do not delay radio power off requests.-->
+ <integer name="config_delay_for_ims_dereg_millis">0</integer>
+
<!--Thresholds for LTE dbm in status bar-->
<integer-array translatable="false" name="config_lteDbmThresholds">
<item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index adb046e..092be40 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -484,6 +484,7 @@
<java-symbol type="string" name="config_deviceSpecificDevicePolicyManagerService" />
<java-symbol type="string" name="config_deviceSpecificAudioService" />
<java-symbol type="integer" name="config_num_physical_slots" />
+ <java-symbol type="integer" name="config_delay_for_ims_dereg_millis" />
<java-symbol type="array" name="config_integrityRuleProviderPackages" />
<java-symbol type="bool" name="config_useAssistantVolume" />
<java-symbol type="string" name="config_bandwidthEstimateSource" />
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 07e4333..0000000
--- 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<AppSearchResult<AppSearchSession>> future = new CompletableFuture<>();
- mAppSearch.createSearchSession(searchContext, mExecutor, future::complete);
- mSearchSession = future.get().getResultValue();
-
- CompletableFuture<AppSearchResult<SetSchemaResponse>> 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<AppSearchBatchResult<String, Void>> putDocumentsFuture =
- new CompletableFuture<>();
-
- // Index the broken document.
- mSearchSession.put(
- new PutDocumentsRequest.Builder().addGenericDocuments(inEmail).build(),
- mExecutor, new BatchResultCallback<String, Void>() {
- @Override
- public void onResult(AppSearchBatchResult<String, Void> 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<AppSearchResult<SetSchemaResponse>> 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<AppSearchBatchResult<String, Void>> putDocumentsFuture =
- new CompletableFuture<>();
- mSearchSession.put(
- new PutDocumentsRequest.Builder().addGenericDocuments(document1).build(),
- mExecutor, new BatchResultCallback<String, Void>() {
- @Override
- public void onResult(AppSearchBatchResult<String, Void> 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<AppSearchResult<List<SearchResult>>> getNextPageFuture =
- new CompletableFuture<>();
- searchResults.getNextPage(mExecutor, getNextPageFuture::complete);
- List<SearchResult> 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<AppSearchResult<SetSchemaResponse>> 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<AppSearchBatchResult<String, Void>> putDocumentsFuture =
- new CompletableFuture<>();
- mSearchSession.put(
- new PutDocumentsRequest.Builder()
- .addGenericDocuments(document1, document2, document3).build(),
- mExecutor, new BatchResultCallback<String, Void>() {
- @Override
- public void onResult(AppSearchBatchResult<String, Void> 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<GenericDocument> outDocs = new ArrayList<>();
- CompletableFuture<AppSearchResult<List<SearchResult>>> getNextPageFuture =
- new CompletableFuture<>();
- searchResults.getNextPage(mExecutor, getNextPageFuture::complete);
- List<SearchResult> 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 24f6b0b..0000000
--- 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 29b0228..0000000
--- 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 228a061..0000000
--- 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 3d820ac..0000000
--- 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 a613e77..0000000
--- 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 0bbea42..0000000
--- 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 1774d72..0000000
--- 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 680ce52..0000000
--- 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<Bundle> listOne = new ArrayList<>(ImmutableList.of(one, two, three));
- ArrayList<Bundle> listOneClone = new ArrayList<>(ImmutableList.of(one, twoClone, three));
- ArrayList<Bundle> 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<Parcelable> array1 = new SparseArray<>();
- array1.put(1, parcelable1);
- array1.put(10, parcelable2);
-
- SparseArray<Parcelable> array1Clone = new SparseArray<>();
- array1Clone.put(1, parcelable1);
- array1Clone.put(10, parcelable2);
-
- SparseArray<Parcelable> 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<Parcelable> array1 = new SparseArray<>();
- array1.put(1, parcelable1);
- array1.put(10, parcelable2);
-
- SparseArray<Parcelable> array1Clone = new SparseArray<>();
- array1Clone.put(1, parcelable1);
- array1Clone.put(10, parcelable2);
-
- SparseArray<Parcelable> 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<Parcelable> 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 057ecbd..0000000
--- 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/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index 4cc70ba..9d2cab3 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -16,16 +16,24 @@
package android.os;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
+import android.util.Log;
+
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Objects;
+
/**
* Unit tests for bundle that requires accessing hidden APS. Tests that can be written only with
* public APIs should go in the CTS counterpart.
@@ -35,6 +43,14 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BundleTest {
+ private Log.TerribleFailureHandler mWtfHandler;
+
+ @After
+ public void tearDown() throws Exception {
+ if (mWtfHandler != null) {
+ Log.setWtfHandler(mWtfHandler);
+ }
+ }
/**
* Take a bundle, write it to a parcel and return the parcel.
@@ -217,4 +233,193 @@
// return true
assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
}
+
+ @Test
+ public void kindofEquals_lazyValues() {
+ Parcelable p1 = new CustomParcelable(13, "Tiramisu");
+ Parcelable p2 = new CustomParcelable(13, "Tiramisu");
+
+ // 2 maps with live objects
+ Bundle a = new Bundle();
+ a.putParcelable("key1", p1);
+ Bundle b = new Bundle();
+ b.putParcelable("key1", p2);
+ assertTrue(Bundle.kindofEquals(a, b));
+
+ // 2 identical parcels
+ a.readFromParcel(getParcelledBundle(a));
+ a.setClassLoader(getClass().getClassLoader());
+ b.readFromParcel(getParcelledBundle(b));
+ b.setClassLoader(getClass().getClassLoader());
+ assertTrue(Bundle.kindofEquals(a, b));
+
+ // 2 lazy values with identical parcels inside
+ a.isEmpty();
+ b.isEmpty();
+ assertTrue(Bundle.kindofEquals(a, b));
+
+ // 1 lazy value vs 1 live object
+ a.getParcelable("key1");
+ assertFalse(Bundle.kindofEquals(a, b));
+
+ // 2 live objects
+ b.getParcelable("key1");
+ assertTrue(Bundle.kindofEquals(a, b));
+ }
+
+ @Test
+ public void kindofEquals_lazyValuesWithIdenticalParcels_returnsTrue() {
+ Parcelable p1 = new CustomParcelable(13, "Tiramisu");
+ Parcelable p2 = new CustomParcelable(13, "Tiramisu");
+ Bundle a = new Bundle();
+ a.putParcelable("key", p1);
+ a.readFromParcel(getParcelledBundle(a));
+ a.setClassLoader(getClass().getClassLoader());
+ Bundle b = new Bundle();
+ b.putParcelable("key", p2);
+ b.readFromParcel(getParcelledBundle(b));
+ b.setClassLoader(getClass().getClassLoader());
+ // 2 lazy values with identical parcels inside
+ a.isEmpty();
+ b.isEmpty();
+
+ assertTrue(Bundle.kindofEquals(a, b));
+ }
+
+ @Test
+ public void kindofEquals_lazyValuesAndDifferentClassLoaders_returnsFalse() {
+ Parcelable p1 = new CustomParcelable(13, "Tiramisu");
+ Parcelable p2 = new CustomParcelable(13, "Tiramisu");
+ Bundle a = new Bundle();
+ a.putParcelable("key", p1);
+ a.readFromParcel(getParcelledBundle(a));
+ a.setClassLoader(getClass().getClassLoader());
+ Bundle b = new Bundle();
+ b.putParcelable("key", p2);
+ b.readFromParcel(getParcelledBundle(b));
+ b.setClassLoader(Bundle.class.getClassLoader()); // BCP
+ // 2 lazy values with identical parcels inside
+ a.isEmpty();
+ b.isEmpty();
+
+ assertFalse(Bundle.kindofEquals(a, b));
+ }
+
+ @Test
+ public void kindofEquals_lazyValuesOfDifferentTypes_returnsFalse() {
+ Parcelable p = new CustomParcelable(13, "Tiramisu");
+ Parcelable[] ps = {p};
+ Bundle a = new Bundle();
+ a.putParcelable("key", p);
+ a.readFromParcel(getParcelledBundle(a));
+ a.setClassLoader(getClass().getClassLoader());
+ Bundle b = new Bundle();
+ b.putParcelableArray("key", ps);
+ b.readFromParcel(getParcelledBundle(b));
+ b.setClassLoader(getClass().getClassLoader());
+ a.isEmpty();
+ b.isEmpty();
+
+ assertFalse(Bundle.kindofEquals(a, b));
+ }
+
+ @Test
+ public void kindofEquals_lazyValuesWithDifferentLengths_returnsFalse() {
+ Parcelable p1 = new CustomParcelable(13, "Tiramisu");
+ Parcelable p2 = new CustomParcelable(13, "Tiramisuuuuuuuu");
+ Bundle a = new Bundle();
+ a.putParcelable("key", p1);
+ a.readFromParcel(getParcelledBundle(a));
+ a.setClassLoader(getClass().getClassLoader());
+ Bundle b = new Bundle();
+ b.putParcelable("key", p2);
+ b.readFromParcel(getParcelledBundle(b));
+ b.setClassLoader(getClass().getClassLoader());
+ a.isEmpty();
+ b.isEmpty();
+
+ assertFalse(Bundle.kindofEquals(a, b));
+ }
+
+ @Test
+ public void readWriteLengthMismatch_logsWtf() throws Exception {
+ mWtfHandler = Log.setWtfHandler((tag, e, system) -> {
+ throw new RuntimeException(e);
+ });
+ Parcelable parcelable = new CustomParcelable(13, "Tiramisu").setHasLengthMismatch(true);
+ Bundle bundle = new Bundle();
+ bundle.putParcelable("p", parcelable);
+ bundle.readFromParcel(getParcelledBundle(bundle));
+ bundle.setClassLoader(getClass().getClassLoader());
+ RuntimeException e = assertThrows(RuntimeException.class, () -> bundle.getParcelable("p"));
+ assertThat(e.getCause()).isInstanceOf(Log.TerribleFailure.class);
+ }
+
+ private static class CustomParcelable implements Parcelable {
+ public final int integer;
+ public final String string;
+ public boolean hasLengthMismatch;
+
+ CustomParcelable(int integer, String string) {
+ this.integer = integer;
+ this.string = string;
+ }
+
+ protected CustomParcelable(Parcel in) {
+ integer = in.readInt();
+ string = in.readString();
+ hasLengthMismatch = in.readBoolean();
+ }
+
+ public CustomParcelable setHasLengthMismatch(boolean hasLengthMismatch) {
+ this.hasLengthMismatch = hasLengthMismatch;
+ return this;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(integer);
+ out.writeString(string);
+ out.writeBoolean(hasLengthMismatch);
+ if (hasLengthMismatch) {
+ out.writeString("extra-write");
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof CustomParcelable)) {
+ return false;
+ }
+ CustomParcelable
+ that = (CustomParcelable) other;
+ return integer == that.integer
+ && hasLengthMismatch == that.hasLengthMismatch
+ && string.equals(that.string);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(integer, string, hasLengthMismatch);
+ }
+
+ public static final Creator<CustomParcelable> CREATOR = new Creator<CustomParcelable>() {
+ @Override
+ public CustomParcelable createFromParcel(Parcel in) {
+ return new CustomParcelable(in);
+ }
+ @Override
+ public CustomParcelable[] newArray(int size) {
+ return new CustomParcelable[size];
+ }
+ };
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 5ecff44..7ce9b51 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -273,7 +273,7 @@
public void disconnect() {
synchronized (mProfileLock) {
- mLocalAdapter.disconnectAllEnabledProfiles(mDevice);
+ mDevice.disconnect();
}
// Disconnect PBAP server in case its connected
// This is to ensure all the profiles are disconnected as some CK/Hs do not
@@ -314,7 +314,7 @@
}
mConnectAttempted = SystemClock.elapsedRealtime();
- connectAllEnabledProfiles();
+ connectDevice();
}
public long getHiSyncId() {
@@ -371,7 +371,7 @@
connect();
}
- private void connectAllEnabledProfiles() {
+ private void connectDevice() {
synchronized (mProfileLock) {
// Try to initialize the profiles if they were not.
if (mProfiles.isEmpty()) {
@@ -386,7 +386,7 @@
return;
}
- mLocalAdapter.connectAllEnabledProfiles(mDevice);
+ mDevice.connect();
}
}
@@ -769,8 +769,8 @@
* Otherwise, allow the connect on UUID change.
*/
if ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime()) {
- Log.d(TAG, "onUuidChanged: triggering connectAllEnabledProfiles");
- connectAllEnabledProfiles();
+ Log.d(TAG, "onUuidChanged: triggering connectDevice");
+ connectDevice();
}
dispatchAttributesChanged();
diff --git a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
index 0aafb29..240ac01 100644
--- a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
@@ -80,7 +80,7 @@
}
@Override
- public void onCancelled(PendingIntent intent) {
+ public void onCanceled(PendingIntent intent) {
if (Log.isLoggable(getOwner().getTag(), Log.DEBUG)) {
Log.d(getOwner().getTag(),
"pending intent registration " + getIdentity() + " canceled");
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 8955c28..d882705 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1089,9 +1089,9 @@
}
@Override
- public void onCancelled(PendingIntent intent) {
+ public void onCanceled(PendingIntent intent) {
if (D) {
- Log.d(TAG, mName + " provider registration " + getIdentity() + " cancelled");
+ Log.d(TAG, mName + " provider registration " + getIdentity() + " canceled");
}
synchronized (mLock) {
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 9926953..0000000
--- 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}.
- *
- * <p>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 088ed27..0000000
--- 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}.
- *
- * <p>Build/Install/Run: atest FrameworksMockingServicesTests:AppSearchConfigTest
- */
-public class FrameworkLimitConfigTest {
- @Rule
- public final TestableDeviceConfig.TestableDeviceConfigRule
- mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
-
- @Test
- public void testDefaultValues() {
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
- FrameworkLimitConfig config = new FrameworkLimitConfig(appSearchConfig);
- assertThat(config.getMaxDocumentSizeBytes()).isEqualTo(
- AppSearchConfig.DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES);
- assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentCount()).isEqualTo(
- AppSearchConfig.DEFAULT_LIMIT_CONFIG_MAX_DOCUMENT_COUNT);
- }
-
- @Test
- public void testCustomizedValues() {
- AppSearchConfig appSearchConfig = AppSearchConfig.create(DIRECT_EXECUTOR);
- FrameworkLimitConfig config = new FrameworkLimitConfig(appSearchConfig);
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_LIMIT_CONFIG_MAX_DOCUMENT_SIZE_BYTES,
- "2001",
- /*makeDefault=*/ false);
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_APPSEARCH,
- AppSearchConfig.KEY_LIMIT_CONFIG_MAX_DOCUMENT_COUNT,
- "2002",
- /*makeDefault=*/ false);
-
- assertThat(config.getMaxDocumentSizeBytes()).isEqualTo(2001);
- assertThat(appSearchConfig.getCachedLimitConfigMaxDocumentCount()).isEqualTo(2002);
- }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS b/services/tests/mockingservicestests/src/com/android/server/appsearch/OWNERS
deleted file mode 100644
index 24f6b0b..0000000
--- 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 28fcaee..0000000
--- 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}.
- *
- * <p>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 0d475c0..0000000
--- 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<UserHandle, PackageManager> 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 8389c85..0000000
--- 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 ebe9e4e..0000000
--- 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 f40a5ad..0000000
--- 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<SchemaTypeConfigProto> 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<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Insert a document 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<AppSearchSchema> 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<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Insert 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<AppSearchSchema> 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<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Insert 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<SchemaTypeConfigProto> existingSchemas =
- mAppSearchImpl.getSchemaProtoLocked().getTypesList();
-
- List<AppSearchSchema> 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<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
- expectedTypes.addAll(existingSchemas);
- expectedTypes.addAll(expectedProto.getTypesList());
- assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
- .containsExactlyElementsIn(expectedTypes);
- }
-
- @Test
- public void testSetSchema_incompatible() throws Exception {
- List<AppSearchSchema> 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<AppSearchSchema> 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<SchemaTypeConfigProto> existingSchemas =
- mAppSearchImpl.getSchemaProtoLocked().getTypesList();
-
- List<AppSearchSchema> 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<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
- expectedTypes.addAll(existingSchemas);
- expectedTypes.addAll(expectedProto.getTypesList());
- assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
- .containsExactlyElementsIn(expectedTypes);
-
- final List<AppSearchSchema> 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<SchemaTypeConfigProto> existingSchemas =
- mAppSearchImpl.getSchemaProtoLocked().getTypesList();
-
- // Create schemas
- List<AppSearchSchema> 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<SchemaTypeConfigProto> 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<SchemaTypeConfigProto> existingSchemas =
- mAppSearchImpl.getSchemaProtoLocked().getTypesList();
- Map<String, Set<String>> existingDatabases = mAppSearchImpl.getPackageToDatabases();
-
- // Insert package schema
- List<AppSearchSchema> 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<SchemaTypeConfigProto> existingSchemas =
- mAppSearchImpl.getSchemaProtoLocked().getTypesList();
- Map<String, Set<String>> existingDatabases = mAppSearchImpl.getPackageToDatabases();
-
- Set<String> 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<AppSearchSchema> 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<SchemaTypeConfigProto> 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<String, Set<String>> existingMapping = mAppSearchImpl.getPackageToDatabases();
- Map<String, Set<String>> 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<String, Map<String, SchemaTypeConfigProto>> 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<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Insert 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<SearchResult> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // 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<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
- mAppSearchImpl.setSchema(
- "package1",
- "database2",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // 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<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- appSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- 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<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- appSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Add 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<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- appSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Add two documents 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<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- appSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Add two documents 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<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- appSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Add two documents
- GenericDocument document1 =
- new GenericDocument.Builder<>("namespace1", "id1", "type").build();
- appSearchImpl.putDocument("package", "database", document1, /*logger=*/ null);
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace1", "id2", "type").build();
- appSearchImpl.putDocument("package", "database", document2, /*logger=*/ null);
-
- StorageInfoProto storageInfo = appSearchImpl.getRawStorageInfoProto();
-
- // Simple checks to verify if we can get correct StorageInfoProto from IcingSearchEngine
- // No need to cover all the fields
- assertThat(storageInfo.getTotalStorageSize()).isGreaterThan(0);
- assertThat(storageInfo.getDocumentStorageInfo().getNumAliveDocuments()).isEqualTo(2);
- assertThat(storageInfo.getSchemaStoreStorageInfo().getNumSchemaTypes()).isEqualTo(1);
- }
-
- @Test
- public void testLimitConfig_DocumentSize() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return 80;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 1;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Insert a document which is too large
- GenericDocument document =
- new GenericDocument.Builder<>(
- "this_namespace_is_long_to_make_the_doc_big", "id", "type")
- .build();
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains(
- "Document \"id\" for package \"package\" serialized to 99 bytes, which"
- + " exceeds limit of 80 bytes");
-
- // Make sure this failure didn't increase our document count. We should still be able to
- // index 1 document.
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "type").build();
- mAppSearchImpl.putDocument("package", "database", document2, /*logger=*/ null);
-
- // Now we should get a failure
- GenericDocument document3 =
- new GenericDocument.Builder<>("namespace", "id3", "type").build();
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document3, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 1 documents");
- }
-
- @Test
- public void testLimitConfig_Init() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- File tempFolder = mTemporaryFolder.newFolder();
- mAppSearchImpl =
- AppSearchImpl.create(
- tempFolder,
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return 80;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 1;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Index a document
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type").build(),
- /*logger=*/ null);
-
- // Now we should get a failure
- GenericDocument document2 =
- new GenericDocument.Builder<>("namespace", "id2", "type").build();
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document2, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 1 documents");
-
- // Close and reinitialize AppSearchImpl
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- tempFolder,
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return 80;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 1;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Make sure the limit is maintained
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document2, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 1 documents");
- }
-
- @Test
- public void testLimitConfig_Remove() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 3;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Index 3 documents
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type").build(),
- /*logger=*/ null);
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id2", "type").build(),
- /*logger=*/ null);
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id3", "type").build(),
- /*logger=*/ null);
-
- // Now we should get a failure
- GenericDocument document4 =
- new GenericDocument.Builder<>("namespace", "id4", "type").build();
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document4, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 3 documents");
-
- // Remove a document that doesn't exist
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.remove(
- "package",
- "database",
- "namespace",
- "id4",
- /*removeStatsBuilder=*/ null));
-
- // Should still fail
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document4, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 3 documents");
-
- // Remove a document that does exist
- mAppSearchImpl.remove(
- "package", "database", "namespace", "id2", /*removeStatsBuilder=*/ null);
-
- // Now doc4 should work
- mAppSearchImpl.putDocument("package", "database", document4, /*logger=*/ null);
-
- // The next one should fail again
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id5", "type")
- .build(),
- /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 3 documents");
- }
-
- @Test
- public void testLimitConfig_DifferentPackages() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- File tempFolder = mTemporaryFolder.newFolder();
- mAppSearchImpl =
- AppSearchImpl.create(
- tempFolder,
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 2;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema(
- "package1",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
- mAppSearchImpl.setSchema(
- "package1",
- "database2",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
- mAppSearchImpl.setSchema(
- "package2",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
- mAppSearchImpl.setSchema(
- "package2",
- "database2",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Index documents in package1/database1
- mAppSearchImpl.putDocument(
- "package1",
- "database1",
- new GenericDocument.Builder<>("namespace", "id1", "type").build(),
- /*logger=*/ null);
- mAppSearchImpl.putDocument(
- "package1",
- "database2",
- new GenericDocument.Builder<>("namespace", "id2", "type").build(),
- /*logger=*/ null);
-
- // Indexing a third doc into package1 should fail (here we use database3)
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package1",
- "database3",
- new GenericDocument.Builder<>("namespace", "id3", "type")
- .build(),
- /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package1\" exceeded limit of 2 documents");
-
- // Indexing a doc into package2 should succeed
- mAppSearchImpl.putDocument(
- "package2",
- "database1",
- new GenericDocument.Builder<>("namespace", "id1", "type").build(),
- /*logger=*/ null);
-
- // Reinitialize to make sure packages are parsed correctly on init
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- tempFolder,
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 2;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // package1 should still be out of space
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package1",
- "database4",
- new GenericDocument.Builder<>("namespace", "id4", "type")
- .build(),
- /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package1\" exceeded limit of 2 documents");
-
- // package2 has room for one more
- mAppSearchImpl.putDocument(
- "package2",
- "database2",
- new GenericDocument.Builder<>("namespace", "id2", "type").build(),
- /*logger=*/ null);
-
- // now package2 really is out of space
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package2",
- "database3",
- new GenericDocument.Builder<>("namespace", "id3", "type")
- .build(),
- /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package2\" exceeded limit of 2 documents");
- }
-
- @Test
- public void testLimitConfig_RemoveByQyery() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 3;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(
- new AppSearchSchema.Builder("type")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("body")
- .setIndexingType(
- AppSearchSchema.StringPropertyConfig
- .INDEXING_TYPE_PREFIXES)
- .setTokenizerType(
- AppSearchSchema.StringPropertyConfig
- .TOKENIZER_TYPE_PLAIN)
- .build())
- .build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Index 3 documents
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type")
- .setPropertyString("body", "tablet")
- .build(),
- /*logger=*/ null);
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id2", "type")
- .setPropertyString("body", "tabby")
- .build(),
- /*logger=*/ null);
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id3", "type")
- .setPropertyString("body", "grabby")
- .build(),
- /*logger=*/ null);
-
- // Now we should get a failure
- GenericDocument document4 =
- new GenericDocument.Builder<>("namespace", "id4", "type").build();
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document4, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 3 documents");
-
- // Run removebyquery, deleting nothing
- mAppSearchImpl.removeByQuery(
- "package",
- "database",
- "nothing",
- new SearchSpec.Builder().build(),
- /*removeStatsBuilder=*/ null);
-
- // Should still fail
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document4, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 3 documents");
-
- // Remove "tab*"
- mAppSearchImpl.removeByQuery(
- "package",
- "database",
- "tab",
- new SearchSpec.Builder().build(),
- /*removeStatsBuilder=*/ null);
-
- // Now doc4 and doc5 should work
- mAppSearchImpl.putDocument("package", "database", document4, /*logger=*/ null);
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id5", "type").build(),
- /*logger=*/ null);
-
- // We only deleted 2 docs so the next one should fail again
- e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id6", "type")
- .build(),
- /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 3 documents");
- }
-
- @Test
- public void testLimitConfig_Replace() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- mTemporaryFolder.newFolder(),
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 2;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(
- new AppSearchSchema.Builder("type")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("body")
- .build())
- .build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Index a document
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type")
- .setPropertyString("body", "id1.orig")
- .build(),
- /*logger=*/ null);
- // Replace it with another doc
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type")
- .setPropertyString("body", "id1.new")
- .build(),
- /*logger=*/ null);
-
- // Index id2. This should pass but only because we check for replacements.
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id2", "type").build(),
- /*logger=*/ null);
-
- // Now we should get a failure on id3
- GenericDocument document3 =
- new GenericDocument.Builder<>("namespace", "id3", "type").build();
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document3, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 2 documents");
- }
-
- @Test
- public void testLimitConfig_ReplaceReinit() throws Exception {
- // Create a new mAppSearchImpl with a lower limit
- mAppSearchImpl.close();
- File tempFolder = mTemporaryFolder.newFolder();
- mAppSearchImpl =
- AppSearchImpl.create(
- tempFolder,
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 2;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Insert schema
- List<AppSearchSchema> schemas =
- Collections.singletonList(
- new AppSearchSchema.Builder("type")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("body")
- .build())
- .build());
- mAppSearchImpl.setSchema(
- "package",
- "database",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0);
-
- // Index a document
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type")
- .setPropertyString("body", "id1.orig")
- .build(),
- /*logger=*/ null);
- // Replace it with another doc
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id1", "type")
- .setPropertyString("body", "id1.new")
- .build(),
- /*logger=*/ null);
-
- // Reinitialize to make sure replacements are correctly accounted for by init
- mAppSearchImpl.close();
- mAppSearchImpl =
- AppSearchImpl.create(
- tempFolder,
- new LimitConfig() {
- @Override
- public int getMaxDocumentSizeBytes() {
- return Integer.MAX_VALUE;
- }
-
- @Override
- public int getMaxDocumentCount() {
- return 2;
- }
- },
- /*initStatsBuilder=*/ null,
- ALWAYS_OPTIMIZE);
-
- // Index id2. This should pass but only because we check for replacements.
- mAppSearchImpl.putDocument(
- "package",
- "database",
- new GenericDocument.Builder<>("namespace", "id2", "type").build(),
- /*logger=*/ null);
-
- // Now we should get a failure on id3
- GenericDocument document3 =
- new GenericDocument.Builder<>("namespace", "id3", "type").build();
- AppSearchException e =
- assertThrows(
- AppSearchException.class,
- () ->
- mAppSearchImpl.putDocument(
- "package", "database", document3, /*logger=*/ null));
- assertThat(e.getResultCode()).isEqualTo(AppSearchResult.RESULT_OUT_OF_SPACE);
- assertThat(e)
- .hasMessageThat()
- .contains("Package \"package\" exceeded limit of 2 documents");
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
deleted file mode 100644
index 7c97687..0000000
--- 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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<AppSearchSchema> 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 204fc54..0000000
--- 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<GenericDocument.Builder<?>>(
- "namespace", "sDocumentProperties1", SCHEMA_TYPE_1)
- .setCreationTimestampMillis(12345L)
- .build();
- private static final GenericDocument DOCUMENT_PROPERTIES_2 =
- new GenericDocument.Builder<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<String, SchemaTypeConfigProto> 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<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<String, PropertyProto.Builder> 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<String> 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<String, SchemaTypeConfigProto> schemaMap =
- ImmutableMap.of(PREFIX + SCHEMA_TYPE_1, schemaTypeConfigProto);
-
- GenericDocument convertedDocument =
- GenericDocumentToProtoConverter.toGenericDocument(documentProto, PREFIX, schemaMap);
-
- GenericDocument expectedDocument =
- new GenericDocument.Builder<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<String, SchemaTypeConfigProto> 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<GenericDocument.Builder<?>>(
- "namespace", "id1", SCHEMA_TYPE_1)
- .setCreationTimestampMillis(5L)
- .setPropertyDocument(
- documentPropertyName,
- new GenericDocument.Builder<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 ebceba4..0000000
--- 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 992961c..0000000
--- 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<String, Map<String, SchemaTypeConfigProto>> 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 c1dc0e4..0000000
--- 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 ec96d6a..0000000
--- 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}.
- *
- * <p>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<UserHandle, PackageManager> 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 374642b..0000000
--- 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<UserHandle, PackageManager> 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;
- }
-}