Merge "HDMICEC: Edit device select API"
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 272e12d..0000000
--- a/apex/appsearch/framework/java/external/android/app/appsearch/AppSearchBatchResult.java
+++ /dev/null
@@ -1,220 +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);
- }
-
- /**
- * Asserts that this {@link AppSearchBatchResult} has no failures.
- *
- * @hide
- */
- public void checkSuccess() {
- if (!isSuccess()) {
- throw new IllegalStateException("AppSearchBatchResult has failures: " + this);
- }
- }
-
- @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 d6d5315..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 4ec2640..0000000
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ /dev/null
@@ -1,1651 +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.Manifest;
-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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- AppSearchUserInstance instance = null;
- int operationSuccessCount = 0;
- int operationFailureCount = 0;
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- 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(targetUser);
- // TODO(b/173532925): Implement logging for statsBuilder
- SetSchemaResponse setSchemaResponse = instance.getAppSearchImpl().setSchema(
- packageName,
- databaseName,
- schemas,
- instance.getVisibilityStore(),
- schemasNotDisplayedBySystem,
- schemasVisibleToPackages,
- forceOverride,
- schemaVersion,
- /*setSchemaStatsBuilder=*/ null);
- ++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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(targetUser);
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(targetUser);
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- AppSearchUserInstance instance = null;
- int operationSuccessCount = 0;
- int operationFailureCount = 0;
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- AppSearchBatchResult.Builder<String, Void> resultBuilder =
- new AppSearchBatchResult.Builder<>();
- instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- AppSearchUserInstance instance = null;
- int operationSuccessCount = 0;
- int operationFailureCount = 0;
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
- new AppSearchBatchResult.Builder<>();
- instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- AppSearchUserInstance instance = null;
- int operationSuccessCount = 0;
- int operationFailureCount = 0;
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- AppSearchUserInstance instance = null;
- int operationSuccessCount = 0;
- int operationFailureCount = 0;
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
-
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(targetUser);
- // TODO(b/173532925): Implement logging for statsBuilder
- SearchResultPage searchResultPage =
- instance.getAppSearchImpl().getNextPage(
- packageName, nextPageToken, /*statsBuilder=*/ null);
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(targetUser);
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(targetUser);
- // 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());
- }
- // TODO(b/173532925): Implement logging for statsBuilder
- searchResultPage = instance.getAppSearchImpl().getNextPage(
- packageName,
- searchResultPage.getNextPageToken(),
- /*statsBuilder=*/ null);
- }
- }
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(targetUser);
-
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(targetUser);
-
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- AppSearchUserInstance instance = null;
- int operationSuccessCount = 0;
- int operationFailureCount = 0;
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- AppSearchBatchResult.Builder<String, Void> resultBuilder =
- new AppSearchBatchResult.Builder<>();
- instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- AppSearchUserInstance instance = null;
- int operationSuccessCount = 0;
- int operationFailureCount = 0;
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstance(targetUser);
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
- EXECUTOR.execute(() -> {
- @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- AppSearchUserInstance instance = null;
- int operationSuccessCount = 0;
- int operationFailureCount = 0;
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- instance = mAppSearchUserInstanceManager.getUserInstance(targetUser);
- 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 callingPid = Binder.getCallingPid();
- int callingUid = Binder.getCallingUid();
-
- EXECUTOR.execute(() -> {
- @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK;
- AppSearchUserInstance instance = null;
- int operationSuccessCount = 0;
- int operationFailureCount = 0;
- try {
- verifyCaller(callingUid, packageName);
-
- // Obtain the user where the client wants to run the operations in. This should
- // end up being the same as userHandle, assuming it is not a special user and
- // the client is allowed to run operations in that user.
- UserHandle targetUser = handleIncomingUser(userHandle, callingPid, callingUid);
- verifyUserUnlocked(targetUser);
-
- Context targetUserContext = mContext.createContextAsUser(targetUser,
- /*flags=*/ 0);
- instance = mAppSearchUserInstanceManager.getOrCreateUserInstance(
- targetUserContext, targetUser, 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());
- }
- }
- });
- }
-
- /** 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.
- *
- * <p>Takes care of checking permissions and if the target is special user, this method will
- * simply throw.
- *
- * @param targetUserHandle The user which the caller is requesting to execute as.
- * @param callingPid The actual pid of the caller as determined by Binder.
- * @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.
- *
- * @throws IllegalArgumentException if the target user is a special user.
- * @throws SecurityException if caller trying to interact across user without
- * {@link Manifest.permission#INTERACT_ACROSS_USERS_FULL}
- */
- @NonNull
- private UserHandle handleIncomingUser(@NonNull UserHandle targetUserHandle, int callingPid,
- int callingUid) {
- UserHandle callingUserHandle = UserHandle.getUserHandleForUid(callingUid);
- if (callingUserHandle.equals(targetUserHandle)) {
- return targetUserHandle;
- }
-
- // Duplicates UserController#ensureNotSpecialUser
- if (targetUserHandle.getIdentifier() < 0) {
- throw new IllegalArgumentException(
- "Call does not support special user " + targetUserHandle);
- }
-
- if (mContext.checkPermission(
- Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- callingPid,
- callingUid) == PackageManager.PERMISSION_GRANTED) {
- return targetUserHandle;
- }
- throw new SecurityException(
- "Permission denied while calling from uid " + callingUid
- + " with " + targetUserHandle + "; Requires permission: "
- + Manifest.permission.INTERACT_ACROSS_USERS_FULL);
- }
-
- /**
- * Verify various aspects of the calling user.
- *
- * @param callingUid Uid of the caller, usually retrieved from Binder for authenticity.
- * @param claimedCallingPackage Package name the caller claims to be.
- */
- private void verifyCaller(int callingUid, @NonNull String claimedCallingPackage) {
- // Obtain the user where the client is running in. Note that this could be different from
- // the userHandle where the client wants to run the AppSearch operation in.
- UserHandle callingUserHandle = UserHandle.getUserHandleForUid(callingUid);
- Context callingUserContext = mContext.createContextAsUser(callingUserHandle,
- /*flags=*/ 0);
-
- verifyCallingPackage(callingUserContext, callingUid, claimedCallingPackage);
- verifyNotInstantApp(callingUserContext, claimedCallingPackage);
- }
-
- /**
- * Check that the caller's supposed package name matches the uid making the call.
- *
- * @throws SecurityException if the package name and uid don't match.
- */
- private void verifyCallingPackage(
- @NonNull Context actualCallingUserContext,
- int actualCallingUid,
- @NonNull String claimedCallingPackage) {
- int claimedCallingUid = PackageUtil.getPackageUid(
- actualCallingUserContext, 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);
- }
- }
-
- /**
- * Ensure instant apps can't make calls to AppSearch.
- *
- * @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);
- }
- }
-
- 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);
- AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstanceOrNull(userHandle);
- if (instance == null) {
- // augment storage info from file
- UserStorageInfo userStorageInfo =
- mAppSearchUserInstanceManager.getOrCreateUserStorageInfoInstance(
- userHandle);
- stats.dataSize +=
- userStorageInfo.getSizeBytesForPackage(packageName);
- } else {
- 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;
- }
- AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstanceOrNull(userHandle);
- if (instance == null) {
- // augment storage info from file
- UserStorageInfo userStorageInfo =
- mAppSearchUserInstanceManager.getOrCreateUserStorageInfoInstance(
- userHandle);
- for (int i = 0; i < packagesForUid.length; i++) {
- stats.dataSize += userStorageInfo.getSizeBytesForPackage(
- packagesForUid[i]);
- }
- } else {
- 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);
- AppSearchUserInstance instance =
- mAppSearchUserInstanceManager.getUserInstanceOrNull(userHandle);
- if (instance == null) {
- // augment storage info from file
- UserStorageInfo userStorageInfo =
- mAppSearchUserInstanceManager.getOrCreateUserStorageInfoInstance(
- userHandle);
- stats.dataSize += userStorageInfo.getTotalSizeBytes();
- } else {
- List<PackageInfo> packagesForUser = mPackageManager.getInstalledPackagesAsUser(
- /*flags=*/0, userHandle.getIdentifier());
- if (packagesForUser != null) {
- 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 ca56325..0000000
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchUserInstanceManager.java
+++ /dev/null
@@ -1,254 +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.annotation.Nullable;
-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<>();
- @GuardedBy("mStorageInfoLocked")
- private final Map<UserHandle, UserStorageInfo> mStorageInfoLocked = 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();
- }
- }
- synchronized (mStorageInfoLocked) {
- mStorageInfoLocked.remove(userHandle);
- }
- }
-
- /**
- * 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 initialized {@link AppSearchUserInstance} for the given user, or {@code null} if
- * no such instance exists.
- *
- * @param userHandle The multi-user handle of the device user calling AppSearch
- */
- @Nullable
- public AppSearchUserInstance getUserInstanceOrNull(@NonNull UserHandle userHandle) {
- Objects.requireNonNull(userHandle);
- synchronized (mInstancesLocked) {
- return mInstancesLocked.get(userHandle);
- }
- }
-
- /**
- * Gets an {@link UserStorageInfo} for the given user.
- *
- * @param userHandle The multi-user handle of the device user
- * @return An initialized {@link UserStorageInfo} for this user
- */
- @NonNull
- public UserStorageInfo getOrCreateUserStorageInfoInstance(@NonNull UserHandle userHandle) {
- Objects.requireNonNull(userHandle);
- synchronized (mStorageInfoLocked) {
- UserStorageInfo userStorageInfo = mStorageInfoLocked.get(userHandle);
- if (userStorageInfo == null) {
- userStorageInfo = new UserStorageInfo(getAppSearchDir(userHandle));
- mStorageInfoLocked.put(userHandle, userStorageInfo);
- }
- return userStorageInfo;
- }
- }
-
- /**
- * 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();
-
- // Update storage info file
- UserStorageInfo userStorageInfo = getOrCreateUserStorageInfoInstance(userHandle);
- userStorageInfo.updateStorageInfoFile(appSearchImpl);
-
- 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/UserStorageInfo.java b/apex/appsearch/service/java/com/android/server/appsearch/UserStorageInfo.java
deleted file mode 100644
index d6e8598..0000000
--- a/apex/appsearch/service/java/com/android/server/appsearch/UserStorageInfo.java
+++ /dev/null
@@ -1,152 +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.server.appsearch.external.localstorage.util.PrefixUtil.getPackageName;
-
-import android.annotation.NonNull;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.appsearch.external.localstorage.AppSearchImpl;
-
-import com.google.android.icing.proto.DocumentStorageInfoProto;
-import com.google.android.icing.proto.NamespaceStorageInfoProto;
-import com.google.android.icing.proto.StorageInfoProto;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-/** Saves the storage info read from file for a user. */
-public final class UserStorageInfo {
- public static final String STORAGE_INFO_FILE = "appsearch_storage";
- private static final String TAG = "AppSearchUserStorage";
- private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
- private final File mStorageInfoFile;
-
- // Saves storage usage byte size for each package under the user, keyed by package name.
- private Map<String, Long> mPackageStorageSizeMap;
- // Saves storage usage byte size for all packages under the user.
- private long mTotalStorageSizeBytes;
-
- public UserStorageInfo(@NonNull File fileParentPath) {
- Objects.requireNonNull(fileParentPath);
- mStorageInfoFile = new File(fileParentPath, STORAGE_INFO_FILE);
- readStorageInfoFromFile();
- }
-
- /**
- * Updates storage info file with the latest storage info queried through
- * {@link AppSearchImpl}.
- */
- public void updateStorageInfoFile(@NonNull AppSearchImpl appSearchImpl) {
- Objects.requireNonNull(appSearchImpl);
- mReadWriteLock.writeLock().lock();
- try (FileOutputStream out = new FileOutputStream(mStorageInfoFile)) {
- appSearchImpl.getRawStorageInfoProto().writeTo(out);
- } catch (Throwable e) {
- Log.w(TAG, "Failed to dump storage info into file", e);
- } finally {
- mReadWriteLock.writeLock().unlock();
- }
- }
-
- /**
- * Gets storage usage byte size for a package with a given package name.
- *
- * <p> Please note the storage info cached in file may be stale.
- */
- public long getSizeBytesForPackage(@NonNull String packageName) {
- Objects.requireNonNull(packageName);
- return mPackageStorageSizeMap.getOrDefault(packageName, 0L);
- }
-
- /**
- * Gets total storage usage byte size for all packages under the user.
- *
- * <p> Please note the storage info cached in file may be stale.
- */
- public long getTotalSizeBytes() {
- return mTotalStorageSizeBytes;
- }
-
- @VisibleForTesting
- void readStorageInfoFromFile() {
- if (mStorageInfoFile.exists()) {
- mReadWriteLock.readLock().lock();
- try (InputStream in = new FileInputStream(mStorageInfoFile)) {
- StorageInfoProto storageInfo = StorageInfoProto.parseFrom(in);
- mTotalStorageSizeBytes = storageInfo.getTotalStorageSize();
- mPackageStorageSizeMap = calculatePackageStorageInfoMap(storageInfo);
- return;
- } catch (Throwable e) {
- Log.w(TAG, "Failed to read storage info from file", e);
- } finally {
- mReadWriteLock.readLock().unlock();
- }
- }
- mTotalStorageSizeBytes = 0;
- mPackageStorageSizeMap = Collections.emptyMap();
- }
-
- /** Calculates storage usage byte size for packages from a {@link StorageInfoProto}. */
- // TODO(b/198553756): Storage cache effort has created two copies of the storage
- // calculation/interpolation logic.
- @NonNull
- @VisibleForTesting
- Map<String, Long> calculatePackageStorageInfoMap(@NonNull StorageInfoProto storageInfo) {
- Map<String, Long> packageStorageInfoMap = new ArrayMap<>();
- if (storageInfo.hasDocumentStorageInfo()) {
- DocumentStorageInfoProto documentStorageInfo = storageInfo.getDocumentStorageInfo();
- List<NamespaceStorageInfoProto> namespaceStorageInfoList =
- documentStorageInfo.getNamespaceStorageInfoList();
-
- Map<String, Integer> packageDocumentCountMap = new ArrayMap<>();
- long totalDocuments = 0;
- for (int i = 0; i < namespaceStorageInfoList.size(); i++) {
- NamespaceStorageInfoProto namespaceStorageInfo = namespaceStorageInfoList.get(i);
- String packageName = getPackageName(namespaceStorageInfo.getNamespace());
- int namespaceDocuments = namespaceStorageInfo.getNumAliveDocuments()
- + namespaceStorageInfo.getNumExpiredDocuments();
- totalDocuments += namespaceDocuments;
- packageDocumentCountMap.put(packageName,
- packageDocumentCountMap.getOrDefault(packageName, 0)
- + namespaceDocuments);
- }
-
- long totalStorageSize = storageInfo.getTotalStorageSize();
- for (Map.Entry<String, Integer> entry : packageDocumentCountMap.entrySet()) {
- // 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 under packages.
- packageStorageInfoMap.put(entry.getKey(),
- (long) (entry.getValue() * 1.0 / totalDocuments * totalStorageSize));
- }
- }
- return Collections.unmodifiableMap(packageStorageInfoMap);
- }
-}
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 324163f..0000000
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ /dev/null
@@ -1,2334 +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.stats.SetSchemaStats;
-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.
- * @param setSchemaStatsBuilder Builder for {@link SetSchemaStats} to hold stats for setSchema
- * @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,
- @Nullable SetSchemaStats.Builder setSchemaStatsBuilder)
- 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);
-
- if (setSchemaStatsBuilder != null) {
- setSchemaStatsBuilder.setStatusCode(
- statusProtoToResultCode(setSchemaResultProto.getStatus()));
- AppSearchLoggerHelper.copyNativeStats(setSchemaResultProto, setSchemaStatsBuilder);
- }
-
- // 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,
- @Nullable SearchStats.Builder statsBuilder)
- throws AppSearchException {
- long totalLatencyStartMillis = SystemClock.elapsedRealtime();
-
- mReadWriteLock.readLock().lock();
- try {
- throwIfClosedLocked();
-
- mLogUtil.piiTrace("getNextPage, request", nextPageToken);
- checkNextPageToken(packageName, nextPageToken);
- SearchResultProto searchResultProto =
- mIcingSearchEngineLocked.getNextPage(nextPageToken);
-
- if (statsBuilder != null) {
- statsBuilder.setStatusCode(statusProtoToResultCode(searchResultProto.getStatus()));
- AppSearchLoggerHelper.copyNativeStats(
- searchResultProto.getQueryStats(), statsBuilder);
- }
-
- 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);
- }
- }
- long rewriteSearchResultLatencyStartMillis = SystemClock.elapsedRealtime();
- SearchResultPage resultPage =
- rewriteSearchResultProto(searchResultProto, mSchemaMapLocked);
- if (statsBuilder != null) {
- statsBuilder.setRewriteSearchResultLatencyMillis(
- (int)
- (SystemClock.elapsedRealtime()
- - rewriteSearchResultLatencyStartMillis));
- }
- return resultPage;
- } finally {
- mReadWriteLock.readLock().unlock();
- if (statsBuilder != null) {
- statsBuilder.setTotalLatencyMillis(
- (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis));
- }
- }
- }
-
- /**
- * 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.getDeleteByQueryStats(), 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.getDeleteByQueryStats().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 1f7d44e..0000000
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLogger.java
+++ /dev/null
@@ -1,62 +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;
-import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
-
-/**
- * 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);
-
- /** Logs {@link SetSchemaStats} */
- void logStats(@NonNull SetSchemaStats 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 c19ba14..0000000
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchLoggerHelper.java
+++ /dev/null
@@ -1,211 +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.android.server.appsearch.external.localstorage.stats.SetSchemaStats;
-
-import com.google.android.icing.proto.DeleteByQueryStatsProto;
-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 com.google.android.icing.proto.SetSchemaResultProto;
-
-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 DeleteByQuery stats to builder.
- *
- * @param fromNativeStats Stats copied from.
- * @param toStatsBuilder Stats copied to.
- */
- static void copyNativeStats(
- @NonNull DeleteByQueryStatsProto fromNativeStats,
- @NonNull RemoveStats.Builder toStatsBuilder) {
- Objects.requireNonNull(fromNativeStats);
- Objects.requireNonNull(toStatsBuilder);
-
- @SuppressWarnings("deprecation")
- int deleteType = DeleteStatsProto.DeleteType.Code.DEPRECATED_QUERY.getNumber();
- toStatsBuilder
- .setNativeLatencyMillis(fromNativeStats.getLatencyMs())
- .setDeleteType(deleteType)
- .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());
- }
-
- /*
- * Copy SetSchema result stats to builder.
- *
- * @param fromProto Stats copied from.
- * @param toStatsBuilder Stats copied to.
- */
- static void copyNativeStats(
- @NonNull SetSchemaResultProto fromProto,
- @NonNull SetSchemaStats.Builder toStatsBuilder) {
- Objects.requireNonNull(fromProto);
- Objects.requireNonNull(toStatsBuilder);
- toStatsBuilder
- .setNewTypeCount(fromProto.getNewSchemaTypesCount())
- .setDeletedTypeCount(fromProto.getDeletedSchemaTypesCount())
- .setCompatibleTypeChangeCount(fromProto.getFullyCompatibleChangedSchemaTypesCount())
- .setIndexIncompatibleTypeChangeCount(
- fromProto.getIndexIncompatibleChangedSchemaTypesCount())
- .setBackwardsIncompatibleTypeChangeCount(
- fromProto.getIncompatibleSchemaTypesCount());
- }
-}
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 75ae2d0a..0000000
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SearchStats.java
+++ /dev/null
@@ -1,465 +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,
- VISIBILITY_SCOPE_UNKNOWN,
- // 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;
- public static final int VISIBILITY_SCOPE_UNKNOWN = 3;
-
- // 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 3e5a80f..0000000
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/stats/SetSchemaStats.java
+++ /dev/null
@@ -1,228 +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;
-
- /** 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;
- 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 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 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 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 4c29ece..0000000
--- a/apex/appsearch/service/java/com/android/server/appsearch/stats/PlatformLogger.java
+++ /dev/null
@@ -1,533 +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.external.localstorage.stats.SetSchemaStats;
-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);
- }
- }
- }
-
- @Override
- public void logStats(@NonNull SetSchemaStats stats) {
- // TODO(b/173532925): Log 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 c4d1016..0000000
--- a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityStoreImpl.java
+++ /dev/null
@@ -1,353 +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,
- /*setSchemaStatsBuilder=*/ null);
- }
-
- // 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 4db8355..0000000
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ /dev/null
@@ -1 +0,0 @@
-bd53b062816070b64feb992c2bf58f3afa3d420e
diff --git a/apex/appsearch/testing/Android.bp b/apex/appsearch/testing/Android.bp
deleted file mode 100644
index f78d98a..0000000
--- a/apex/appsearch/testing/Android.bp
+++ /dev/null
@@ -1,40 +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",
- "service-appsearch",
- "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 4d8e8e9..0000000
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
+++ /dev/null
@@ -1,150 +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.annotation.NonNull;
-import android.annotation.Nullable;
-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 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.external.localstorage.stats.SetSchemaStats;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Future;
-
-public class AppSearchTestUtils {
- // Non-thread-safe logger implementation for testing
- public static class TestLogger implements AppSearchLogger {
- @Nullable public CallStats mCallStats;
- @Nullable public PutDocumentStats mPutDocumentStats;
- @Nullable public InitializeStats mInitializeStats;
- @Nullable public SearchStats mSearchStats;
- @Nullable public RemoveStats mRemoveStats;
- @Nullable public OptimizeStats mOptimizeStats;
- @Nullable public SetSchemaStats mSetSchemaStats;
-
- @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;
- }
-
- @Override
- public void logStats(@NonNull SetSchemaStats stats) {
- mSetSchemaStats = stats;
- }
- }
-
- 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/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 20a93fd..31a5a050 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -910,6 +910,11 @@
}
return standbyChanged || tareChanged;
});
+ if (!USE_TARE_POLICY) {
+ // Remove the cached values so we don't accidentally use them when TARE is
+ // re-enabled.
+ mAffordabilityCache.clear();
+ }
if (changed) {
rescheduleKernelAlarmsLocked();
updateNextAlarmClockLocked();
@@ -1383,6 +1388,7 @@
* @param uid uid to filter on
* @param packageName package to filter on, or null for all packages in uid
*/
+ @GuardedBy("mLock")
void sendPendingBackgroundAlarmsLocked(int uid, String packageName) {
final ArrayList<Alarm> alarmsForUid = mPendingBackgroundAlarms.get(uid);
if (alarmsForUid == null || alarmsForUid.size() == 0) {
@@ -1419,6 +1425,7 @@
*
* This is only called when the power save whitelist changes, so it's okay to be slow.
*/
+ @GuardedBy("mLock")
void sendAllUnrestrictedPendingBackgroundAlarmsLocked() {
final ArrayList<Alarm> alarmsToDeliver = new ArrayList<>();
@@ -1455,6 +1462,7 @@
}
}
+ @GuardedBy("mLock")
private void deliverPendingBackgroundAlarmsLocked(ArrayList<Alarm> alarms, long nowELAPSED) {
final int N = alarms.size();
boolean hasWakeup = false;
@@ -2131,6 +2139,7 @@
}
}
+ @GuardedBy("mLock")
private void setImplLocked(int type, long when, long whenElapsed, long windowLength,
long interval, PendingIntent operation, IAlarmListener directReceiver,
String listenerTag, int flags, WorkSource workSource,
@@ -2408,12 +2417,13 @@
return;
}
mEconomyManagerInternal.registerAffordabilityChangeListener(
- UserHandle.getUserId(alarm.uid), alarm.sourcePackage,
+ UserHandle.getUserId(alarm.creatorUid), alarm.sourcePackage,
mAffordabilityChangeListener, TareBill.getAppropriateBill(alarm));
}
/** Unregister the TARE listener associated with the alarm if it's no longer needed. */
- private void maybeUnregisterTareListener(Alarm alarm) {
+ @GuardedBy("mLock")
+ private void maybeUnregisterTareListenerLocked(Alarm alarm) {
if (!mConstants.USE_TARE_POLICY) {
return;
}
@@ -2423,12 +2433,21 @@
&& alarm.sourcePackage.equals(a.sourcePackage)
&& bill.equals(TareBill.getAppropriateBill(a));
if (mAlarmStore.getCount(isSameAlarmTypeForSameApp) == 0) {
+ final int userId = UserHandle.getUserId(alarm.creatorUid);
mEconomyManagerInternal.unregisterAffordabilityChangeListener(
- UserHandle.getUserId(alarm.uid), alarm.sourcePackage,
+ userId, alarm.sourcePackage,
mAffordabilityChangeListener, bill);
+ // Remove the cached value so we don't accidentally use it when the app
+ // schedules a new alarm.
+ ArrayMap<EconomyManagerInternal.ActionBill, Boolean> actionAffordability =
+ mAffordabilityCache.get(userId, alarm.sourcePackage);
+ if (actionAffordability != null) {
+ actionAffordability.remove(bill);
+ }
}
}
+ @GuardedBy("mLock")
private void setImplLocked(Alarm a) {
if ((a.flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) {
adjustIdleUntilTime(a);
@@ -3777,6 +3796,7 @@
*
* This is not expected to get called frequently.
*/
+ @GuardedBy("mLock")
void removeExactAlarmsOnPermissionRevokedLocked(int uid, String packageName) {
if (isExemptFromExactAlarmPermission(uid)
|| !isExactAlarmChangeEnabled(packageName, UserHandle.getUserId(uid))) {
@@ -3794,6 +3814,7 @@
}
}
+ @GuardedBy("mLock")
private void removeAlarmsInternalLocked(Predicate<Alarm> whichAlarms, int reason) {
final long nowRtc = mInjector.getCurrentTimeMillis();
final long nowElapsed = mInjector.getElapsedRealtime();
@@ -3834,7 +3855,7 @@
mRemovalHistory.put(removed.uid, bufferForUid);
}
bufferForUid.append(new RemovedAlarm(removed, reason, nowRtc, nowElapsed));
- maybeUnregisterTareListener(removed);
+ maybeUnregisterTareListenerLocked(removed);
}
if (removedFromStore) {
@@ -3859,6 +3880,7 @@
}
}
+ @GuardedBy("mLock")
void removeLocked(PendingIntent operation, IAlarmListener directReceiver, int reason) {
if (operation == null && directReceiver == null) {
if (localLOGV) {
@@ -3870,6 +3892,7 @@
removeAlarmsInternalLocked(a -> a.matches(operation, directReceiver), reason);
}
+ @GuardedBy("mLock")
void removeLocked(final int uid, int reason) {
if (uid == Process.SYSTEM_UID) {
// If a force-stop occurs for a system-uid package, ignore it.
@@ -3878,6 +3901,7 @@
removeAlarmsInternalLocked(a -> a.uid == uid, reason);
}
+ @GuardedBy("mLock")
void removeLocked(final String packageName) {
if (packageName == null) {
if (localLOGV) {
@@ -3890,6 +3914,7 @@
}
// Only called for ephemeral apps
+ @GuardedBy("mLock")
void removeForStoppedLocked(final int uid) {
if (uid == Process.SYSTEM_UID) {
// If a force-stop occurs for a system-uid package, ignore it.
@@ -3900,6 +3925,7 @@
removeAlarmsInternalLocked(whichAlarms, REMOVE_REASON_UNDEFINED);
}
+ @GuardedBy("mLock")
void removeUserLocked(int userHandle) {
if (userHandle == USER_SYSTEM) {
Slog.w(TAG, "Ignoring attempt to remove system-user state!");
@@ -3926,6 +3952,7 @@
}
}
+ @GuardedBy("mLock")
void interactiveStateChangedLocked(boolean interactive) {
if (mInteractive != interactive) {
mInteractive = interactive;
@@ -4035,6 +4062,7 @@
private static native int setKernelTimezone(long nativeData, int minuteswest);
private static native long getNextAlarm(long nativeData, int type);
+ @GuardedBy("mLock")
int triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED) {
int wakeUps = 0;
final ArrayList<Alarm> pendingAlarms = mAlarmStore.removePendingAlarms(nowELAPSED);
@@ -4152,6 +4180,7 @@
return timeSinceLast <= currentNonWakeupFuzzLocked(nowELAPSED);
}
+ @GuardedBy("mLock")
void deliverAlarmsLocked(ArrayList<Alarm> triggerList, long nowELAPSED) {
mLastAlarmDeliveryTime = nowELAPSED;
for (int i = 0; i < triggerList.size(); i++) {
@@ -4175,7 +4204,7 @@
reportAlarmEventToTare(alarm);
if (alarm.repeatInterval <= 0) {
// Don't bother trying to unregister for a repeating alarm.
- maybeUnregisterTareListener(alarm);
+ maybeUnregisterTareListenerLocked(alarm);
}
} catch (RuntimeException e) {
Slog.w(TAG, "Failure sending alarm.", e);
@@ -4186,6 +4215,9 @@
}
private void reportAlarmEventToTare(Alarm alarm) {
+ if (!mConstants.USE_TARE_POLICY) {
+ return;
+ }
final boolean allowWhileIdle =
(alarm.flags & (FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED | FLAG_ALLOW_WHILE_IDLE)) != 0;
final int action;
@@ -4224,7 +4256,7 @@
}
}
mEconomyManagerInternal.noteInstantaneousEvent(
- UserHandle.getUserId(alarm.uid), alarm.sourcePackage, action, null);
+ UserHandle.getUserId(alarm.creatorUid), alarm.sourcePackage, action, null);
}
@VisibleForTesting
@@ -4539,7 +4571,7 @@
@GuardedBy("mLock")
private boolean canAffordBillLocked(@NonNull Alarm alarm,
@NonNull EconomyManagerInternal.ActionBill bill) {
- final int userId = UserHandle.getUserId(alarm.uid);
+ final int userId = UserHandle.getUserId(alarm.creatorUid);
final String pkgName = alarm.sourcePackage;
ArrayMap<EconomyManagerInternal.ActionBill, Boolean> actionAffordability =
mAffordabilityCache.get(userId, pkgName);
@@ -5058,6 +5090,7 @@
class DeliveryTracker extends IAlarmCompleteListener.Stub implements PendingIntent.OnFinished {
+ @GuardedBy("mLock")
private InFlight removeLocked(PendingIntent pi, Intent intent) {
for (int i = 0; i < mInFlight.size(); i++) {
final InFlight inflight = mInFlight.get(i);
@@ -5072,6 +5105,7 @@
return null;
}
+ @GuardedBy("mLock")
private InFlight removeLocked(IBinder listener) {
for (int i = 0; i < mInFlight.size(); i++) {
if (mInFlight.get(i).mListener == listener) {
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index 180424f..ad56d93 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -76,28 +76,17 @@
* regularly reclaiming ARCs from it.
*/
private static final long MIN_UNUSED_TIME_MS = 3 * 24 * HOUR_IN_MILLIS;
- /**
- * The maximum amount of time we'll keep a transaction around for.
- * For now, only keep transactions we actually have a use for. We can increase it if we want
- * to use older transactions or provide older transactions to apps.
- */
- private static final long MAX_TRANSACTION_AGE_MS = 24 * HOUR_IN_MILLIS;
- /** The maximum number of transactions to dump per ledger. */
- private static final int MAX_NUM_TRANSACTION_DUMP = 25;
private static final String ALARM_TAG_AFFORDABILITY_CHECK = "*tare.affordability_check*";
- private static final String ALARM_TAG_LEDGER_CLEANUP = "*tare.ledger_cleanup*";
private final Object mLock;
private final Handler mHandler;
private final InternalResourceService mIrs;
+ private final Scribe mScribe;
private final AppStandbyInternal mAppStandbyInternal;
@GuardedBy("mLock")
- private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>();
-
- @GuardedBy("mLock")
private final SparseArrayMap<String, SparseArrayMap<String, OngoingEvent>>
mCurrentOngoingEvents = new SparseArrayMap<>();
@@ -113,16 +102,6 @@
private final SparseArrayMap<String, ArraySet<ActionAffordabilityNote>>
mActionAffordabilityNotes = new SparseArrayMap<>();
- @GuardedBy("mLock")
- private long mCurrentNarcsInCirculation;
-
- /**
- * Listener to track and manage when we remove old transactions from ledgers.
- */
- @GuardedBy("mLock")
- private final LedgerCleanupAlarmListener mLedgerCleanupAlarmListener =
- new LedgerCleanupAlarmListener();
-
/**
* Listener to track and manage when apps will cross the closest affordability threshold (in
* both directions).
@@ -180,28 +159,16 @@
};
private static final int MSG_CHECK_BALANCE = 0;
- private static final int MSG_CLEAN_LEDGER = 1;
- private static final int MSG_SET_ALARMS = 2;
+ private static final int MSG_SET_BALANCE_ALARM = 1;
- Agent(@NonNull InternalResourceService irs) {
+ Agent(@NonNull InternalResourceService irs, @NonNull Scribe scribe) {
mLock = irs.getLock();
mIrs = irs;
+ mScribe = scribe;
mHandler = new AgentHandler(TareHandlerThread.get().getLooper());
mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
}
- @GuardedBy("mLock")
- @NonNull
- private Ledger getLedgerLocked(final int userId, @NonNull final String pkgName) {
- Ledger ledger = mLedgers.get(userId, pkgName);
- if (ledger == null) {
- // TODO: load from disk
- ledger = new Ledger();
- mLedgers.add(userId, pkgName, ledger);
- }
- return ledger;
- }
-
private class TotalDeltaCalculator implements Consumer<OngoingEvent> {
private Ledger mLedger;
private long mNowElapsed;
@@ -227,7 +194,7 @@
/** Get an app's current balance, factoring in any currently ongoing events. */
@GuardedBy("mLock")
long getBalanceLocked(final int userId, @NonNull final String pkgName) {
- final Ledger ledger = getLedgerLocked(userId, pkgName);
+ final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
long balance = ledger.getCurrentBalance();
SparseArrayMap<String, OngoingEvent> ongoingEvents =
mCurrentOngoingEvents.get(userId, pkgName);
@@ -241,12 +208,6 @@
return balance;
}
- /** Returns the total amount of narcs currently allocated to apps. */
- @GuardedBy("mLock")
- long getCurrentCirculationLocked() {
- return mCurrentNarcsInCirculation;
- }
-
@GuardedBy("mLock")
void noteInstantaneousEventLocked(final int userId, @NonNull final String pkgName,
final int eventId, @Nullable String tag) {
@@ -256,7 +217,7 @@
}
final long now = getCurrentTimeMillis();
- final Ledger ledger = getLedgerLocked(userId, pkgName);
+ final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
final int eventType = getEventType(eventId);
@@ -364,7 +325,7 @@
for (int i = 0; i < size; ++i) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
final long originalBalance =
- getLedgerLocked(userId, pkgName).getCurrentBalance();
+ mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
wasAffordable[i] = originalBalance >= note.getCachedModifiedPrice();
}
} else {
@@ -385,7 +346,8 @@
for (int i = 0; i < size; ++i) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(i);
note.recalculateModifiedPrice(economicPolicy, userId, pkgName);
- final long newBalance = getLedgerLocked(userId, pkgName).getCurrentBalance();
+ final long newBalance =
+ mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
if (wasAffordable[i] != isAffordable) {
note.setNewAffordability(isAffordable);
@@ -417,7 +379,7 @@
for (int n = 0; n < size; ++n) {
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
final long originalBalance =
- getLedgerLocked(userId, pkgName).getCurrentBalance();
+ mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
wasAffordable[n] = originalBalance >= note.getCachedModifiedPrice();
}
} else {
@@ -439,7 +401,7 @@
final ActionAffordabilityNote note = actionAffordabilityNotes.valueAt(n);
note.recalculateModifiedPrice(economicPolicy, userId, pkgName);
final long newBalance =
- getLedgerLocked(userId, pkgName).getCurrentBalance();
+ mScribe.getLedgerLocked(userId, pkgName).getCurrentBalance();
final boolean isAffordable = newBalance >= note.getCachedModifiedPrice();
if (wasAffordable[n] != isAffordable) {
note.setNewAffordability(isAffordable);
@@ -473,7 +435,7 @@
return;
}
- final Ledger ledger = getLedgerLocked(userId, pkgName);
+ final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
SparseArrayMap<String, OngoingEvent> ongoingEvents =
mCurrentOngoingEvents.get(userId, pkgName);
@@ -538,11 +500,12 @@
}
final CompleteEconomicPolicy economicPolicy = mIrs.getCompleteEconomicPolicyLocked();
final long maxCirculationAllowed = mIrs.getMaxCirculationLocked();
- final long newArcsInCirculation = mCurrentNarcsInCirculation + transaction.delta;
+ final long curNarcsInCirculation = mScribe.getNarcsInCirculationLocked();
+ final long newArcsInCirculation = curNarcsInCirculation + transaction.delta;
if (transaction.delta > 0 && newArcsInCirculation > maxCirculationAllowed) {
// Set lower bound at 0 so we don't accidentally take away credits when we were trying
// to _give_ the app credits.
- final long newDelta = Math.max(0, maxCirculationAllowed - mCurrentNarcsInCirculation);
+ final long newDelta = Math.max(0, maxCirculationAllowed - curNarcsInCirculation);
Slog.i(TAG, "Would result in too many credits in circulation. Decreasing transaction "
+ eventToString(transaction.eventId)
+ (transaction.tag == null ? "" : ":" + transaction.tag)
@@ -569,16 +532,8 @@
transaction.eventId, transaction.tag, newDelta);
}
ledger.recordTransaction(transaction);
- mCurrentNarcsInCirculation += transaction.delta;
- if (!mLedgerCleanupAlarmListener.hasAlarmScheduledLocked(userId, pkgName)) {
- // The earliest transaction won't change until we clean up the ledger, so no point
- // continuing to reschedule an existing cleanup.
- final long cleanupAlarmElapsed = SystemClock.elapsedRealtime() + MAX_TRANSACTION_AGE_MS
- - (getCurrentTimeMillis() - ledger.getEarliestTransaction().endTimeMs);
- mLedgerCleanupAlarmListener.addAlarmLocked(userId, pkgName, cleanupAlarmElapsed);
- }
- // TODO: save changes to disk in a background thread
- if (notifyOnAffordabilityChange) {
+ mScribe.adjustNarcsInCirculationLocked(transaction.delta);
+ if (transaction.delta != 0 && notifyOnAffordabilityChange) {
final ArraySet<ActionAffordabilityNote> actionAffordabilityNotes =
mActionAffordabilityNotes.get(userId, pkgName);
if (actionAffordabilityNotes != null) {
@@ -611,7 +566,7 @@
for (int i = 0; i < pkgs.size(); ++i) {
final int userId = UserHandle.getUserId(pkgs.get(i).applicationInfo.uid);
final String pkgName = pkgs.get(i).packageName;
- final Ledger ledger = getLedgerLocked(userId, pkgName);
+ final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
// AppStandby only counts elapsed time for things like this
// TODO: should we use clock time instead?
final long timeSinceLastUsedMs =
@@ -662,7 +617,7 @@
}
final int userId = UserHandle.getUserId(pkgInfo.applicationInfo.uid);
final String pkgName = pkgInfo.packageName;
- Ledger ledger = getLedgerLocked(userId, pkgName);
+ final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
final double perc = batteryLevel / 100d;
// TODO: maybe don't give credits to bankrupt apps until battery level >= 50%
@@ -701,7 +656,7 @@
continue;
}
final String pkgName = packageInfo.packageName;
- final Ledger ledger = getLedgerLocked(userId, pkgName);
+ final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
if (ledger.getCurrentBalance() > 0) {
// App already got credits somehow. Move along.
Slog.wtf(TAG, "App " + pkgName + " had credits before economy was set up");
@@ -717,7 +672,7 @@
@GuardedBy("mLock")
void grantBirthrightLocked(final int userId, @NonNull final String pkgName) {
- final Ledger ledger = getLedgerLocked(userId, pkgName);
+ final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
if (ledger.getCurrentBalance() > 0) {
Slog.wtf(TAG, "App " + pkgName + " had credits as soon as it was installed");
// App already got credits somehow. Move along.
@@ -737,7 +692,6 @@
@GuardedBy("mLock")
void onPackageRemovedLocked(final int userId, @NonNull final String pkgName) {
reclaimAssetsLocked(userId, pkgName);
- mLedgerCleanupAlarmListener.removeAlarmLocked(userId, pkgName);
mBalanceThresholdAlarmListener.removeAlarmLocked(userId, pkgName);
}
@@ -747,19 +701,17 @@
*/
@GuardedBy("mLock")
private void reclaimAssetsLocked(final int userId, @NonNull final String pkgName) {
- Ledger ledger = getLedgerLocked(userId, pkgName);
+ final Ledger ledger = mScribe.getLedgerLocked(userId, pkgName);
if (ledger.getCurrentBalance() != 0) {
- mCurrentNarcsInCirculation -= ledger.getCurrentBalance();
+ mScribe.adjustNarcsInCirculationLocked(-ledger.getCurrentBalance());
}
- // TODO: delete ledger entry from disk
- mLedgers.delete(userId, pkgName);
+ mScribe.discardLedgerLocked(userId, pkgName);
mCurrentOngoingEvents.delete(userId, pkgName);
}
@GuardedBy("mLock")
void onUserRemovedLocked(final int userId, @NonNull final List<String> pkgNames) {
reclaimAssetsLocked(userId, pkgNames);
- mLedgerCleanupAlarmListener.removeAlarmsLocked(userId);
mBalanceThresholdAlarmListener.removeAlarmsLocked(userId);
}
@@ -891,11 +843,8 @@
@GuardedBy("mLock")
void tearDownLocked() {
- mLedgers.clear();
- mCurrentNarcsInCirculation = 0;
mCurrentOngoingEvents.clear();
mBalanceThresholdAlarmListener.dropAllAlarmsLocked();
- mLedgerCleanupAlarmListener.dropAllAlarmsLocked();
}
@VisibleForTesting
@@ -1096,7 +1045,7 @@
mAlarmTag, this, mHandler);
}
} else {
- mHandler.sendEmptyMessageDelayed(MSG_SET_ALARMS, 30_000);
+ mHandler.sendEmptyMessageDelayed(MSG_SET_BALANCE_ALARM, 30_000);
}
});
mTriggerTimeElapsed = nextTriggerTimeElapsed;
@@ -1165,20 +1114,6 @@
}
}
- /** Clean up old transactions from {@link Ledger}s. */
- private class LedgerCleanupAlarmListener extends AlarmQueueListener {
- private LedgerCleanupAlarmListener() {
- // We don't need to run cleanup too frequently.
- super(ALARM_TAG_LEDGER_CLEANUP, false, HOUR_IN_MILLIS);
- }
-
- @Override
- @GuardedBy("mLock")
- protected void processExpiredAlarmLocked(int userId, @NonNull String packageName) {
- mHandler.obtainMessage(MSG_CLEAN_LEDGER, userId, 0, packageName).sendToTarget();
- }
- }
-
/** Track when apps will cross the closest affordability threshold (in both directions). */
private class BalanceThresholdAlarmListener extends AlarmQueueListener {
private BalanceThresholdAlarmListener() {
@@ -1354,19 +1289,8 @@
}
break;
- case MSG_CLEAN_LEDGER: {
- final int userId = msg.arg1;
- final String pkgName = (String) msg.obj;
+ case MSG_SET_BALANCE_ALARM: {
synchronized (mLock) {
- final Ledger ledger = getLedgerLocked(userId, pkgName);
- ledger.removeOldTransactions(MAX_TRANSACTION_AGE_MS);
- }
- }
- break;
-
- case MSG_SET_ALARMS: {
- synchronized (mLock) {
- mLedgerCleanupAlarmListener.setNextAlarmLocked();
mBalanceThresholdAlarmListener.setNextAlarmLocked();
}
}
@@ -1377,24 +1301,7 @@
@GuardedBy("mLock")
void dumpLocked(IndentingPrintWriter pw) {
- pw.println("Ledgers:");
- pw.increaseIndent();
- mLedgers.forEach((userId, pkgName, ledger) -> {
- pw.print(appToString(userId, pkgName));
- if (mIrs.isSystem(userId, pkgName)) {
- pw.print(" (system)");
- }
- pw.println();
- pw.increaseIndent();
- ledger.dump(pw, MAX_NUM_TRANSACTION_DUMP);
- pw.decreaseIndent();
- });
- pw.decreaseIndent();
-
pw.println();
mBalanceThresholdAlarmListener.dumpLocked(pw);
-
- pw.println();
- mLedgerCleanupAlarmListener.dumpLocked(pw);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index b3b6f6f..e9fa926 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -109,7 +109,7 @@
@NonNull
@GuardedBy("mLock")
- private List<PackageInfo> mPkgCache = new ArrayList<>();
+ private final List<PackageInfo> mPkgCache = new ArrayList<>();
/** Cached mapping of UIDs (for all users) to a list of packages in the UID. */
@GuardedBy("mLock")
@@ -222,7 +222,7 @@
mEconomyManagerStub = new EconomyManagerStub();
mScribe = new Scribe(this);
mCompleteEconomicPolicy = new CompleteEconomicPolicy(this);
- mAgent = new Agent(this);
+ mAgent = new Agent(this, mScribe);
mConfigObserver = new ConfigObserver(mHandler, context);
@@ -859,7 +859,7 @@
pw.print("/");
pw.println(narcToString(mCompleteEconomicPolicy.getMaxSatiatedCirculation()));
- final long currentCirculation = mAgent.getCurrentCirculationLocked();
+ final long currentCirculation = mScribe.getNarcsInCirculationLocked();
pw.print("Current GDP: ");
pw.print(narcToString(currentCirculation));
pw.print(" (");
@@ -870,6 +870,9 @@
mCompleteEconomicPolicy.dump(pw);
pw.println();
+ mScribe.dumpLocked(pw);
+
+ pw.println();
mAgent.dumpLocked(pw);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
index a2c1e9a..2c133dcb 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -16,7 +16,14 @@
package com.android.server.tare;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+
+import static com.android.server.tare.TareUtils.appToString;
+
+import android.annotation.NonNull;
+import android.util.IndentingPrintWriter;
import android.util.Log;
+import android.util.SparseArrayMap;
import com.android.internal.annotations.GuardedBy;
@@ -28,27 +35,124 @@
private static final boolean DEBUG = InternalResourceService.DEBUG
|| Log.isLoggable(TAG, Log.DEBUG);
+ /** The maximum number of transactions to dump per ledger. */
+ private static final int MAX_NUM_TRANSACTION_DUMP = 25;
+ /**
+ * The maximum amount of time we'll keep a transaction around for.
+ * For now, only keep transactions we actually have a use for. We can increase it if we want
+ * to use older transactions or provide older transactions to apps.
+ */
+ private static final long MAX_TRANSACTION_AGE_MS = 24 * HOUR_IN_MILLIS;
+
private final InternalResourceService mIrs;
@GuardedBy("mIrs.mLock")
private long mLastReclamationTime;
+ @GuardedBy("mIrs.mLock")
+ private long mNarcsInCirculation;
+ @GuardedBy("mIrs.mLock")
+ private final SparseArrayMap<String, Ledger> mLedgers = new SparseArrayMap<>();
+
+ private final Runnable mCleanRunnable = this::cleanupLedgers;
Scribe(InternalResourceService irs) {
mIrs = irs;
}
@GuardedBy("mIrs.mLock")
+ void adjustNarcsInCirculationLocked(long delta) {
+ if (delta != 0) {
+ // No point doing any work if the change is 0.
+ mNarcsInCirculation += delta;
+ }
+ }
+
+ @GuardedBy("mIrs.mLock")
+ void discardLedgerLocked(final int userId, @NonNull final String pkgName) {
+ mLedgers.delete(userId, pkgName);
+ }
+
+ @GuardedBy("mIrs.mLock")
long getLastReclamationTimeLocked() {
return mLastReclamationTime;
}
- @GuardedBy("InternalResourceService.mLock")
+ @GuardedBy("mIrs.mLock")
+ @NonNull
+ Ledger getLedgerLocked(final int userId, @NonNull final String pkgName) {
+ Ledger ledger = mLedgers.get(userId, pkgName);
+ if (ledger == null) {
+ ledger = new Ledger();
+ mLedgers.add(userId, pkgName, ledger);
+ }
+ return ledger;
+ }
+
+ /** Returns the total amount of narcs currently allocated to apps. */
+ @GuardedBy("mIrs.mLock")
+ long getNarcsInCirculationLocked() {
+ return mNarcsInCirculation;
+ }
+
+ @GuardedBy("mIrs.mLock")
void setLastReclamationTimeLocked(long time) {
mLastReclamationTime = time;
}
@GuardedBy("mIrs.mLock")
void tearDownLocked() {
+ mLedgers.clear();
+ mNarcsInCirculation = 0;
mLastReclamationTime = 0;
}
+
+ private void scheduleCleanup(long earliestEndTime) {
+ if (earliestEndTime == Long.MAX_VALUE) {
+ return;
+ }
+ // This is just cleanup to manage memory. We don't need to do it too often or at the exact
+ // intended real time, so the delay that comes from using the Handler (and is limited
+ // to uptime) should be fine.
+ final long delayMs = Math.max(HOUR_IN_MILLIS,
+ earliestEndTime + MAX_TRANSACTION_AGE_MS - System.currentTimeMillis());
+ TareHandlerThread.getHandler().postDelayed(mCleanRunnable, delayMs);
+ }
+
+ private void cleanupLedgers() {
+ synchronized (mIrs.getLock()) {
+ TareHandlerThread.getHandler().removeCallbacks(mCleanRunnable);
+ long earliestEndTime = Long.MAX_VALUE;
+ for (int uIdx = mLedgers.numMaps() - 1; uIdx >= 0; --uIdx) {
+ final int userId = mLedgers.keyAt(uIdx);
+
+ for (int pIdx = mLedgers.numElementsForKey(userId) - 1; pIdx >= 0; --pIdx) {
+ final String pkgName = mLedgers.keyAt(uIdx, pIdx);
+ final Ledger ledger = mLedgers.get(userId, pkgName);
+ ledger.removeOldTransactions(MAX_TRANSACTION_AGE_MS);
+ Ledger.Transaction transaction = ledger.getEarliestTransaction();
+ if (transaction != null) {
+ earliestEndTime = Math.min(earliestEndTime, transaction.endTimeMs);
+ }
+ }
+ }
+ scheduleCleanup(earliestEndTime);
+ }
+ }
+
+ @GuardedBy("mIrs.mLock")
+ void dumpLocked(IndentingPrintWriter pw) {
+ pw.println("Ledgers:");
+ pw.increaseIndent();
+ mLedgers.forEach((userId, pkgName, ledger) -> {
+ pw.print(appToString(userId, pkgName));
+ if (mIrs.isSystem(userId, pkgName)) {
+ pw.print(" (system)");
+ }
+ pw.println();
+ pw.increaseIndent();
+ ledger.dump(pw, MAX_NUM_TRANSACTION_DUMP);
+ pw.decreaseIndent();
+ });
+ pw.decreaseIndent();
+ }
}
diff --git a/apex/media/Android.bp b/apex/media/Android.bp
index f2e64ce..1a710a98b 100644
--- a/apex/media/Android.bp
+++ b/apex/media/Android.bp
@@ -28,8 +28,8 @@
sdk {
name: "media-module-sdk",
bootclasspath_fragments: ["com.android.media-bootclasspath-fragment"],
+ systemserverclasspath_fragments: ["com.android.media-systemserverclasspath-fragment"],
java_sdk_libs: [
"framework-media",
- "service-media-s",
],
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 7a7d191..8d8f7b0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -119,6 +119,7 @@
field public static final String NFC_TRANSACTION_EVENT = "android.permission.NFC_TRANSACTION_EVENT";
field public static final String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
field @Deprecated public static final String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
+ field public static final String POST_NOTIFICATIONS = "android.permission.POST_NOTIFICATIONS";
field @Deprecated public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
field public static final String QUERY_ALL_PACKAGES = "android.permission.QUERY_ALL_PACKAGES";
field public static final String READ_CALENDAR = "android.permission.READ_CALENDAR";
@@ -204,6 +205,7 @@
field public static final String LOCATION = "android.permission-group.LOCATION";
field public static final String MICROPHONE = "android.permission-group.MICROPHONE";
field public static final String NEARBY_DEVICES = "android.permission-group.NEARBY_DEVICES";
+ field public static final String NOTIFICATIONS = "android.permission-group.NOTIFICATIONS";
field public static final String PHONE = "android.permission-group.PHONE";
field public static final String SENSORS = "android.permission-group.SENSORS";
field public static final String SMS = "android.permission-group.SMS";
@@ -1301,7 +1303,7 @@
field public static final int shortcutLongLabel = 16844074; // 0x101052a
field public static final int shortcutShortLabel = 16844073; // 0x1010529
field public static final int shouldDisableView = 16843246; // 0x10101ee
- field public static final int shouldUseDefaultDeviceStateChangeTransition;
+ field public static final int shouldUseDefaultDisplayStateChangeTransition;
field public static final int showAsAction = 16843481; // 0x10102d9
field public static final int showDefault = 16843258; // 0x10101fa
field public static final int showDividers = 16843561; // 0x1010329
@@ -6941,7 +6943,7 @@
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public CharSequence loadLabel(android.content.pm.PackageManager);
method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
- method public boolean shouldUseDefaultDeviceStateChangeTransition();
+ method public boolean shouldUseDefaultDisplayStateChangeTransition();
method public boolean supportsMultipleDisplays();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
@@ -31577,12 +31579,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);
@@ -31721,7 +31726,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
}
@@ -35270,6 +35275,7 @@
field public static final String ACTION_ACCESSIBILITY_SETTINGS = "android.settings.ACCESSIBILITY_SETTINGS";
field public static final String ACTION_ADD_ACCOUNT = "android.settings.ADD_ACCOUNT_SETTINGS";
field public static final String ACTION_AIRPLANE_MODE_SETTINGS = "android.settings.AIRPLANE_MODE_SETTINGS";
+ field public static final String ACTION_ALL_APPS_NOTIFICATION_SETTINGS = "android.settings.ALL_APPS_NOTIFICATION_SETTINGS";
field public static final String ACTION_APN_SETTINGS = "android.settings.APN_SETTINGS";
field public static final String ACTION_APPLICATION_DETAILS_SETTINGS = "android.settings.APPLICATION_DETAILS_SETTINGS";
field public static final String ACTION_APPLICATION_DEVELOPMENT_SETTINGS = "android.settings.APPLICATION_DEVELOPMENT_SETTINGS";
@@ -39102,6 +39108,13 @@
package android.service.voice {
+ public final class VisibleActivityInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.service.voice.VoiceInteractionSession.ActivityId getActivityId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.service.voice.VisibleActivityInfo> CREATOR;
+ }
+
public class VoiceInteractionService extends android.app.Service {
ctor public VoiceInteractionService();
method public int getDisabledShowContext();
@@ -39163,6 +39176,7 @@
method public void onTaskStarted(android.content.Intent, int);
method public void onTrimMemory(int);
method public final void performDirectAction(@NonNull android.app.DirectAction, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.os.Bundle>);
+ method public final void registerVisibleActivityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback);
method public final void requestDirectActions(@NonNull android.service.voice.VoiceInteractionSession.ActivityId, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.DirectAction>>);
method public void setContentView(android.view.View);
method public void setDisabledShowContext(int);
@@ -39172,6 +39186,7 @@
method public void show(android.os.Bundle, int);
method public void startAssistantActivity(android.content.Intent);
method public void startVoiceActivity(android.content.Intent);
+ method public final void unregisterVisibleActivityCallback(@NonNull android.service.voice.VoiceInteractionSession.VisibleActivityCallback);
field public static final int SHOW_SOURCE_ACTIVITY = 16; // 0x10
field public static final int SHOW_SOURCE_APPLICATION = 8; // 0x8
field public static final int SHOW_SOURCE_ASSIST_GESTURE = 4; // 0x4
@@ -39245,6 +39260,11 @@
method public boolean isActive();
}
+ public static interface VoiceInteractionSession.VisibleActivityCallback {
+ method public default void onInvisible(@NonNull android.service.voice.VoiceInteractionSession.ActivityId);
+ method public default void onVisible(@NonNull android.service.voice.VisibleActivityInfo);
+ }
+
public abstract class VoiceInteractionSessionService extends android.app.Service {
ctor public VoiceInteractionSessionService();
method public android.os.IBinder onBind(android.content.Intent);
@@ -50935,6 +50955,7 @@
method @Nullable public android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo getTouchDelegateInfo();
method public android.view.accessibility.AccessibilityNodeInfo getTraversalAfter();
method public android.view.accessibility.AccessibilityNodeInfo getTraversalBefore();
+ method @Nullable public String getUniqueId();
method public String getViewIdResourceName();
method public android.view.accessibility.AccessibilityWindowInfo getWindow();
method public int getWindowId();
@@ -51029,6 +51050,7 @@
method public void setTraversalAfter(android.view.View, int);
method public void setTraversalBefore(android.view.View);
method public void setTraversalBefore(android.view.View, int);
+ method public void setUniqueId(@Nullable String);
method public void setViewIdResourceName(String);
method public void setVisibleToUser(boolean);
method public void writeToParcel(android.os.Parcel, int);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 3ceb8873..64d4456 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -15,6 +15,10 @@
method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public boolean updateMccMncConfiguration(@NonNull String, @NonNull String);
}
+ public class ActivityOptions {
+ method @NonNull public static android.app.ActivityOptions fromBundle(@NonNull android.os.Bundle);
+ }
+
public class AppOpsManager {
field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
}
@@ -42,7 +46,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 d5abadb..9e47e96 100755
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1941,10 +1941,12 @@
public final class BluetoothAdapter {
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean addOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
+ method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean disable(boolean);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean disableBLE();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enableBLE();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean enableNoAutoConnect();
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void generateLocalOobData(int, @NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.OobDataCallback);
+ method @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public java.util.List<android.bluetooth.BluetoothDevice> getActiveDevices(int);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public long getDiscoveryEndMillis();
method public boolean isBleScanAlwaysAvailable();
method public boolean isLeEnabled();
@@ -1985,8 +1987,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();
@@ -8754,15 +8758,15 @@
method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public int getUserSwitchability();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean hasRestrictedProfiles();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean hasUserRestrictionForUser(@NonNull String, @NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isAdminUser();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isAdminUser();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isCloneProfile();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isGuestUser();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isManagedProfile(int);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isMediaSharedWithParent();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isPrimaryUser();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional=true) public boolean isProfile();
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile();
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean isRestrictedProfile(@NonNull android.os.UserHandle);
+ method public boolean isRestrictedProfile();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional=true) public boolean isRestrictedProfile(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isSameProfileGroup(@NonNull android.os.UserHandle, @NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED}) public boolean isUserNameSet();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isUserOfType(@NonNull String);
@@ -9143,6 +9147,10 @@
field public static final int ERROR_UNKNOWN = 0; // 0x0
}
+ public static final class ContactsContract.CommonDataKinds.Phone implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
+ field @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public static final android.net.Uri ENTERPRISE_CONTENT_URI;
+ }
+
@Deprecated public static final class ContactsContract.MetadataSync implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncColumns {
field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata";
field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index ac28903..b14b6d7 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -140,6 +140,7 @@
}
public class ActivityOptions {
+ method @NonNull public static android.app.ActivityOptions fromBundle(@NonNull android.os.Bundle);
method @NonNull public static android.app.ActivityOptions makeCustomAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method @NonNull @RequiresPermission(android.Manifest.permission.START_TASKS_FROM_RECENTS) public static android.app.ActivityOptions makeCustomTaskAnimation(@NonNull android.content.Context, int, int, @Nullable android.os.Handler, @Nullable android.app.ActivityOptions.OnAnimationStartedListener, @Nullable android.app.ActivityOptions.OnAnimationFinishedListener);
method public static void setExitTransitionTimeout(long);
@@ -321,7 +322,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 {
@@ -454,6 +455,7 @@
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void resetDefaultCrossProfileIntentFilters(int);
method @RequiresPermission(allOf={"android.permission.MANAGE_DEVICE_ADMINS", android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
+ method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int);
method @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public void setNextOperationSafety(int, int);
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED";
@@ -2134,7 +2136,7 @@
}
public static final class ContactsContract.CommonDataKinds.Phone implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
- field public static final android.net.Uri ENTERPRISE_CONTENT_URI;
+ field @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public static final android.net.Uri ENTERPRISE_CONTENT_URI;
}
public static final class ContactsContract.PinnedPositions {
@@ -2414,6 +2416,10 @@
method @RequiresPermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD}) public void triggerHardwareRecognitionEventForTest(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[]);
}
+ public final class VisibleActivityInfo implements android.os.Parcelable {
+ ctor public VisibleActivityInfo(int, @NonNull android.os.IBinder);
+ }
+
}
package android.service.watchdog {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 13517ba..e3909f9 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -672,4 +672,23 @@
public abstract int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code,
Intent intent, String resolvedType,
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options);
+
+ /**
+ * Sets the provider to communicate between voice interaction manager service and
+ * ActivityManagerService.
+ */
+ public abstract void setVoiceInteractionManagerProvider(
+ @Nullable VoiceInteractionManagerProvider provider);
+
+ /**
+ * Provides the interface to communicate between voice interaction manager service and
+ * ActivityManagerService.
+ */
+ public interface VoiceInteractionManagerProvider {
+ /**
+ * Notifies the service when a high-level activity event has been changed, for example,
+ * an activity was resumed or stopped.
+ */
+ void notifyActivityEventChanged();
+ }
}
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 763a65f..860224c 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -26,6 +26,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.ExitTransitionCoordinator.ActivityExitTransitionCallbacks;
import android.app.ExitTransitionCoordinator.ExitTransitionCallbacks;
@@ -1368,9 +1369,17 @@
return mRemoteTransition;
}
- /** @hide */
- public static ActivityOptions fromBundle(Bundle bOptions) {
- return bOptions != null ? new ActivityOptions(bOptions) : null;
+ /**
+ * Creates an ActivityOptions from the Bundle generated from {@link ActivityOptions#toBundle()}.
+ * Returns an instance of ActivityOptions populated with options with known keys from the
+ * provided Bundle, stripping out unknown entries.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ @NonNull
+ public static ActivityOptions fromBundle(@NonNull Bundle bOptions) {
+ return new ActivityOptions(bOptions);
}
/** @hide */
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 4b87a64..f5b3b40 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -871,6 +871,7 @@
if (view.isAttachedToWindow()) {
tempMatrix.reset();
mSharedElementParentMatrices.get(i).invert(tempMatrix);
+ decor.transformMatrixToLocal(tempMatrix);
GhostView.addGhost(view, decor, tempMatrix);
ViewGroup parent = (ViewGroup) view.getParent();
if (moveWithParent && !isInTransitionGroup(parent, decor)) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d932a29..a17a89b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1819,6 +1819,8 @@
OP_BLUETOOTH_CONNECT,
OP_BLUETOOTH_ADVERTISE,
OP_UWB_RANGING,
+ // Notifications
+ OP_POST_NOTIFICATION,
// APPOP PERMISSIONS
OP_ACCESS_NOTIFICATIONS,
@@ -2227,7 +2229,7 @@
android.Manifest.permission.READ_CALENDAR,
android.Manifest.permission.WRITE_CALENDAR,
android.Manifest.permission.ACCESS_WIFI_STATE,
- null, // no permission required for notifications
+ android.Manifest.permission.POST_NOTIFICATIONS,
null, // neighboring cells shares the coarse location perm
android.Manifest.permission.CALL_PHONE,
android.Manifest.permission.READ_SMS,
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1ab6e97..bd43ab5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1780,6 +1780,12 @@
}
}
try {
+ ActivityThread thread = ActivityThread.currentActivityThread();
+ Instrumentation instrumentation = thread.getInstrumentation();
+ if (instrumentation.isInstrumenting()
+ && ((flags & Context.RECEIVER_NOT_EXPORTED) == 0)) {
+ flags = flags | Context.RECEIVER_EXPORTED;
+ }
final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, userId,
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/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index c552cb6..a969b10 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -81,7 +81,7 @@
final int mContextDescriptionResource;
final boolean mShowMetadataInPreview;
final boolean mSupportsAmbientMode;
- final boolean mShouldUseDefaultDeviceStateChangeTransition;
+ final boolean mShouldUseDefaultDisplayStateChangeTransition;
final String mSettingsSliceUri;
final boolean mSupportMultipleDisplays;
@@ -146,9 +146,9 @@
mSupportsAmbientMode = sa.getBoolean(
com.android.internal.R.styleable.Wallpaper_supportsAmbientMode,
false);
- mShouldUseDefaultDeviceStateChangeTransition = sa.getBoolean(
+ mShouldUseDefaultDisplayStateChangeTransition = sa.getBoolean(
com.android.internal.R.styleable
- .Wallpaper_shouldUseDefaultDeviceStateChangeTransition, true);
+ .Wallpaper_shouldUseDefaultDisplayStateChangeTransition, true);
mSettingsSliceUri = sa.getString(
com.android.internal.R.styleable.Wallpaper_settingsSliceUri);
mSupportMultipleDisplays = sa.getBoolean(
@@ -175,7 +175,7 @@
mSupportsAmbientMode = source.readInt() != 0;
mSettingsSliceUri = source.readString();
mSupportMultipleDisplays = source.readInt() != 0;
- mShouldUseDefaultDeviceStateChangeTransition = source.readInt() != 0;
+ mShouldUseDefaultDisplayStateChangeTransition = source.readInt() != 0;
mService = ResolveInfo.CREATOR.createFromParcel(source);
}
@@ -400,24 +400,24 @@
/**
* Returns whether this wallpaper should receive default zooming updates when the device
- * changes its state (e.g. when folding or unfolding a foldable device).
+ * changes its display state (e.g. when folding or unfolding a foldable device).
* If set to false the wallpaper will not receive zoom events when changing the device state,
* so it can implement its own transition instead.
* <p>
* This corresponds to the value {@link
- * android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition} in the
+ * android.R.styleable#Wallpaper_shouldUseDefaultDisplayStateChangeTransition} in the
* XML description of the wallpaper.
* <p>
* The default value is {@code true}.
*
- * @see android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition
+ * @see android.R.styleable#Wallpaper_shouldUseDefaultDisplayStateChangeTransition
* @return {@code true} if wallpaper should receive default device state change
* transition updates
*
- * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultDeviceStateChangeTransition
+ * @attr ref android.R.styleable#Wallpaper_shouldUseDefaultDisplayStateChangeTransition
*/
- public boolean shouldUseDefaultDeviceStateChangeTransition() {
- return mShouldUseDefaultDeviceStateChangeTransition;
+ public boolean shouldUseDefaultDisplayStateChangeTransition() {
+ return mShouldUseDefaultDisplayStateChangeTransition;
}
public void dump(Printer pw, String prefix) {
@@ -450,7 +450,7 @@
dest.writeInt(mSupportsAmbientMode ? 1 : 0);
dest.writeString(mSettingsSliceUri);
dest.writeInt(mSupportMultipleDisplays ? 1 : 0);
- dest.writeInt(mShouldUseDefaultDeviceStateChangeTransition ? 1 : 0);
+ dest.writeInt(mShouldUseDefaultDisplayStateChangeTransition ? 1 : 0);
mService.writeToParcel(dest, flags);
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index f572576..6cea2a4 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -7708,54 +7708,64 @@
}
/**
- * @hide
* Sets the given package as the device owner.
- * Same as {@link #setDeviceOwner(ComponentName, String)} but without setting a device owner name.
- * @param who the component name to be registered as device owner.
- * @return whether the package was successfully registered as the device owner.
- * @throws IllegalArgumentException if the package name is null or invalid
- * @throws IllegalStateException If the preconditions mentioned are not met.
- */
- public boolean setDeviceOwner(ComponentName who) {
- return setDeviceOwner(who, null);
- }
-
- /**
- * @hide
- */
- public boolean setDeviceOwner(ComponentName who, int userId) {
- return setDeviceOwner(who, null, userId);
- }
-
- /**
- * @hide
- */
- public boolean setDeviceOwner(ComponentName who, String ownerName) {
- return setDeviceOwner(who, ownerName, UserHandle.USER_SYSTEM);
- }
-
- /**
- * @hide
- * Sets the given package as the device owner. The package must already be installed. There
- * must not already be a device owner.
- * Only apps with the MANAGE_PROFILE_AND_DEVICE_OWNERS permission and the shell uid can call
- * this method.
- * Calling this after the setup phase of the primary user has completed is allowed only if
- * the caller is the shell uid, and there are no additional users and no accounts.
+ *
+ * <p>Preconditions:
+ * <ul>
+ * <li>The package must already be installed.
+ * <li>There must not already be a device owner.
+ * <li>Only apps with the {@code MANAGE_PROFILE_AND_DEVICE_OWNERS} permission or the
+ * {@link Process#SHELL_UID Shell UID} can call this method.
+ * </ul>
+ *
+ * <p>Calling this after the setup phase of the device owner user has completed is allowed only
+ * if the caller is the {@link Process#SHELL_UID Shell UID}, and there are no additional users
+ * (except when the device runs on headless system user mode, in which case it could have exact
+ * one extra user, which is the current user - the device owner will be set in the
+ * {@link UserHandle#SYSTEM system} user and a profile owner will be set in the current user)
+ * and no accounts.
+ *
* @param who the component name to be registered as device owner.
* @param ownerName the human readable name of the institution that owns this device.
* @param userId ID of the user on which the device owner runs.
+ *
* @return whether the package was successfully registered as the device owner.
- * @throws IllegalArgumentException if the package name is null or invalid
+ *
+ * @throws IllegalArgumentException if the package name is {@code null} or invalid.
* @throws IllegalStateException If the preconditions mentioned are not met.
+ *
+ * @hide
*/
@TestApi
@RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
- public boolean setDeviceOwner(
+ public boolean setDeviceOwner(@NonNull ComponentName who, @Nullable String ownerName,
+ @UserIdInt int userId) {
+ if (mService != null) {
+ try {
+ return mService.setDeviceOwner(who, ownerName, userId,
+ /* setProfileOwnerOnCurrentUserIfNecessary= */ true);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Same as {@link #setDeviceOwner(ComponentName, String, int)}, but without setting the profile
+ * owner on current user when running on headless system user mode - should be used only by
+ * testing infra.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public boolean setDeviceOwnerOnly(
@NonNull ComponentName who, @Nullable String ownerName, @UserIdInt int userId) {
if (mService != null) {
try {
- return mService.setDeviceOwner(who, ownerName, userId);
+ return mService.setDeviceOwner(who, ownerName, userId,
+ /* setProfileOwnerOnCurrentUserIfNecessary= */ false);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index cb196a6..ade1190 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -159,7 +159,7 @@
void reportKeyguardDismissed(int userHandle);
void reportKeyguardSecured(int userHandle);
- boolean setDeviceOwner(in ComponentName who, String ownerName, int userId);
+ boolean setDeviceOwner(in ComponentName who, String ownerName, int userId, boolean setProfileOwnerOnCurrentUserIfNecessary);
ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
boolean hasDeviceOwner();
String getDeviceOwnerName();
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 37cbccb..019d5ba 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -187,7 +187,7 @@
in.readPersistableBundle(getClass().getClassLoader()),
in.createTypedArrayList(ResultInfo.CREATOR),
in.createTypedArrayList(ReferrerIntent.CREATOR),
- ActivityOptions.fromBundle(in.readBundle()), in.readBoolean(),
+ readActivityOptions(in), in.readBoolean(),
in.readTypedObject(ProfilerInfo.CREATOR),
in.readStrongBinder(),
IActivityClientController.Stub.asInterface(in.readStrongBinder()),
@@ -196,6 +196,11 @@
in.readStrongBinder());
}
+ private static ActivityOptions readActivityOptions(Parcel in) {
+ Bundle bundle = in.readBundle();
+ return bundle != null ? ActivityOptions.fromBundle(bundle) : null;
+ }
+
public static final @NonNull Creator<LaunchActivityItem> CREATOR =
new Creator<LaunchActivityItem>() {
public LaunchActivityItem createFromParcel(Parcel in) {
diff --git a/core/java/android/app/servertransaction/StartActivityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java
index 15f65f6..f267060 100644
--- a/core/java/android/app/servertransaction/StartActivityItem.java
+++ b/core/java/android/app/servertransaction/StartActivityItem.java
@@ -23,6 +23,7 @@
import android.app.ActivityOptions;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
+import android.os.Bundle;
import android.os.Parcel;
import android.os.Trace;
@@ -83,7 +84,8 @@
/** Read from Parcel. */
private StartActivityItem(Parcel in) {
- mActivityOptions = ActivityOptions.fromBundle(in.readBundle());
+ Bundle bundle = in.readBundle();
+ mActivityOptions = bundle != null ? ActivityOptions.fromBundle(bundle) : null;
}
public static final @NonNull Creator<StartActivityItem> CREATOR =
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 8aa2785..dd147cc 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -552,6 +552,10 @@
inflateAsync(rvToApply);
return;
}
+ // Prepare a local reference to the remote Context so we're ready to
+ // inflate any requested LayoutParams.
+ mRemoteContext = getRemoteContext();
+
int layoutId = rvToApply.getLayoutId();
if (rvToApply.canRecycleView(mView)) {
try {
@@ -727,6 +731,9 @@
} catch (NameNotFoundException e) {
Log.e(TAG, "Package name " + mInfo.providerInfo.packageName + " not found");
return mContext;
+ } catch (NullPointerException e) {
+ Log.e(TAG, "Error trying to create the remote context.", e);
+ return mContext;
}
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 77abbe9..3b744a7 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -511,6 +511,12 @@
@SystemApi
public static final int ACTIVE_DEVICE_ALL = 2;
+ /** @hide */
+ @IntDef({BluetoothProfile.HEADSET, BluetoothProfile.A2DP,
+ BluetoothProfile.HEARING_AID})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ActiveDeviceProfile {}
+
/**
* Broadcast Action: The local Bluetooth adapter has started the remote
* device discovery process.
@@ -1245,13 +1251,17 @@
/**
* Turn off the local Bluetooth adapter and don't persist the setting.
*
+ * @param persist Indicate whether the off state should be persisted following the next reboot
* @return true to indicate adapter shutdown has begun, or false on immediate error
* @hide
*/
- @UnsupportedAppUsage(trackingBug = 171933273)
+ @SystemApi
@RequiresLegacyBluetoothAdminPermission
@RequiresBluetoothConnectPermission
- @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
public boolean disable(boolean persist) {
try {
@@ -2005,28 +2015,40 @@
}
/**
- * 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}
+ * Get the active devices for the BluetoothProfile specified
*
- * @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
- *
+ * @param profile is the profile from which we want the active devices.
+ * Possible values are:
+ * {@link BluetoothProfile#HEADSET},
+ * {@link BluetoothProfile#A2DP},
+ * {@link BluetoothProfile#HEARING_AID}
+ * @return A list of active bluetooth devices
+ * @throws IllegalArgumentException If profile is not one of {@link ActiveDeviceProfile}
* @hide
*/
- @RequiresBluetoothConnectPermission
+ @SystemApi
@RequiresPermission(allOf = {
android.Manifest.permission.BLUETOOTH_CONNECT,
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
- android.Manifest.permission.MODIFY_PHONE_STATE,
})
- public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) {
+ public @NonNull List<BluetoothDevice> getActiveDevices(@ActiveDeviceProfile int profile) {
+ if (profile != BluetoothProfile.HEADSET
+ && profile != BluetoothProfile.A2DP
+ && profile != BluetoothProfile.HEARING_AID) {
+ Log.e(TAG, "Invalid profile param value in getActiveDevices");
+ throw new IllegalArgumentException("Profiles must be one of "
+ + "BluetoothProfile.A2DP, "
+ + "BluetoothProfile.HEARING_AID, or"
+ + "BluetoothProfile.HEARING_AID");
+ }
try {
mServiceLock.readLock().lock();
if (mService != null) {
- return mService.connectAllEnabledProfiles(device, mAttributionSource);
+ if (DBG) {
+ Log.d(TAG, "getActiveDevices(profile= "
+ + BluetoothProfile.getProfileName(profile) + ")");
+ }
+ return mService.getActiveDevices(profile, mAttributionSource);
}
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -2034,39 +2056,7 @@
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 new ArrayList<>();
}
/**
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 71f05f5..6e918bd 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1683,6 +1683,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/content/pm/permission/CompatibilityPermissionInfo.java b/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
index 9198b95..7370823 100644
--- a/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
+++ b/core/java/android/content/pm/permission/CompatibilityPermissionInfo.java
@@ -39,10 +39,15 @@
/**
* List of new permissions that have been added since 1.0.
*
+ * NOTE: These must be declared in SDK version order, with permissions
+ * added to newer SDKs appearing before those added to older SDKs.
+ *
* @hide
*/
public static final CompatibilityPermissionInfo[] COMPAT_PERMS =
new CompatibilityPermissionInfo[]{
+ new CompatibilityPermissionInfo(Manifest.permission.POST_NOTIFICATIONS,
+ android.os.Build.VERSION_CODES.TIRAMISU, 0 /*usesPermissionFlags*/),
new CompatibilityPermissionInfo(Manifest.permission.WRITE_EXTERNAL_STORAGE,
android.os.Build.VERSION_CODES.DONUT, 0 /*usesPermissionFlags*/),
new CompatibilityPermissionInfo(Manifest.permission.READ_PHONE_STATE,
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 01e237d..7e61b48 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -228,7 +228,9 @@
mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount);
} catch (SQLiteCantOpenDatabaseException e) {
final StringBuilder message = new StringBuilder("Cannot open database '")
- .append(file).append('\'');
+ .append(file).append('\'')
+ .append(" with flags 0x")
+ .append(Integer.toHexString(mConfiguration.openFlags));
try {
// Try to diagnose for common reasons. If something fails in here, that's fine;
@@ -236,21 +238,27 @@
final Path path = FileSystems.getDefault().getPath(file);
final Path dir = path.getParent();
-
- if (!Files.isDirectory(dir)) {
+ if (dir == null) {
+ message.append(": Directory not specified in the file path");
+ } else if (!Files.isDirectory(dir)) {
message.append(": Directory ").append(dir).append(" doesn't exist");
} else if (!Files.exists(path)) {
- message.append(": File ").append(path).append(" doesn't exist");
+ message.append(": File ").append(path).append(
+ " doesn't exist");
+ if ((mConfiguration.openFlags & SQLiteDatabase.CREATE_IF_NECESSARY) != 0) {
+ message.append(
+ " and CREATE_IF_NECESSARY is set, check directory permissions");
+ }
} else if (!Files.isReadable(path)) {
message.append(": File ").append(path).append(" is not readable");
} else if (Files.isDirectory(path)) {
message.append(": Path ").append(path).append(" is a directory");
} else {
- message.append(": Unknown reason");
+ message.append(": Unable to deduct failure reason");
}
} catch (Throwable th) {
- message.append(": Unknown reason; cannot examine filesystem: ")
- .append(th.getMessage());
+ message.append(": Unable to deduct failure reason"
+ + " because filesystem couldn't be examined: ").append(th.getMessage());
}
throw new SQLiteCantOpenDatabaseException(message.toString(), e);
} finally {
diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
index a1f7aa1..518b22b 100644
--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java
+++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java
@@ -22,6 +22,7 @@
import android.os.SystemProperties;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.Log;
import com.android.internal.R;
@@ -32,7 +33,7 @@
*/
@TestApi
public class AmbientDisplayConfiguration {
-
+ private static final String TAG = "AmbientDisplayConfig";
private final Context mContext;
private final boolean mAlwaysOnByDefault;
@@ -141,11 +142,20 @@
}
/** {@hide} */
- public String tapSensorType() {
+ private String tapSensorType() {
return mContext.getResources().getString(R.string.config_dozeTapSensorType);
}
/** {@hide} */
+ public String tapSensorType(int posture) {
+ return getSensorFromPostureMapping(
+ mContext.getResources().getStringArray(R.array.config_dozeTapSensorPostureMapping),
+ tapSensorType(),
+ posture
+ );
+ }
+
+ /** {@hide} */
public String longPressSensorType() {
return mContext.getResources().getString(R.string.config_dozeLongPressSensorType);
}
@@ -241,4 +251,19 @@
private boolean boolSetting(String name, int user, int def) {
return Settings.Secure.getIntForUser(mContext.getContentResolver(), name, def, user) != 0;
}
+
+ /** {@hide} */
+ public static String getSensorFromPostureMapping(
+ String[] postureMapping,
+ String defaultValue,
+ int posture) {
+ String sensorType = defaultValue;
+ if (posture < postureMapping.length) {
+ sensorType = postureMapping[posture];
+ } else {
+ Log.e(TAG, "Unsupported doze posture " + posture);
+ }
+
+ return TextUtils.isEmpty(sensorType) ? defaultValue : sensorType;
+ }
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 209ee40e..c7f3983 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1326,6 +1326,23 @@
*/
String KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS =
"fixed_refresh_rate_high_ambient_brightness_thresholds";
+
+ /**
+ * Key for refresh rate when the device is in high brightness mode for sunlight visility.
+ *
+ * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+ * @see android.R.integer#config_defaultRefreshRateInHbmSunlight
+ */
+ String KEY_REFRESH_RATE_IN_HBM_SUNLIGHT = "refresh_rate_in_hbm_sunlight";
+
+ /**
+ * Key for refresh rate when the device is in high brightness mode for HDR.
+ *
+ * @see android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER
+ * @see android.R.integer#config_defaultRefreshRateInHbmHdr
+ */
+ String KEY_REFRESH_RATE_IN_HBM_HDR = "refresh_rate_in_hbm_hdr";
+
/**
* Key for default peak refresh rate
*
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index a6e77023..4ecd15e 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -66,7 +66,17 @@
public abstract class AbstractInputMethodService extends WindowProviderService
implements KeyEvent.Callback {
private InputMethod mInputMethod;
-
+
+ /**
+ * Keep the strong reference to {@link InputMethodServiceInternal} to ensure that it will not be
+ * garbage-collected until {@link AbstractInputMethodService} gets garbage-collected.
+ *
+ * <p>This is necessary because {@link RemoteInputConnection} internally uses
+ * {@link java.lang.ref.WeakReference} to hold {@link InputMethodServiceInternal}.</p>
+ */
+ @Nullable
+ private InputMethodServiceInternal mInputMethodServiceInternal;
+
final KeyEvent.DispatcherState mDispatcherState
= new KeyEvent.DispatcherState();
@@ -225,7 +235,10 @@
if (mInputMethod == null) {
mInputMethod = onCreateInputMethodInterface();
}
- return new IInputMethodWrapper(createInputMethodServiceInternal(), mInputMethod);
+ if (mInputMethodServiceInternal == null) {
+ mInputMethodServiceInternal = createInputMethodServiceInternal();
+ }
+ return new IInputMethodWrapper(mInputMethodServiceInternal, mInputMethod);
}
/**
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/DeadSystemRuntimeException.java b/core/java/android/os/DeadSystemRuntimeException.java
new file mode 100644
index 0000000..1e86924
--- /dev/null
+++ b/core/java/android/os/DeadSystemRuntimeException.java
@@ -0,0 +1,31 @@
+/*
+ * 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.os;
+
+/**
+ * Exception thrown when a call into system_server resulted in a
+ * DeadObjectException, meaning that the system_server has died. There's
+ * nothing apps can do at this point - the system will automatically restart -
+ * so there's no point in catching this.
+ *
+ * @hide
+ */
+public class DeadSystemRuntimeException extends RuntimeException {
+ public DeadSystemRuntimeException() {
+ super(new DeadSystemException());
+ }
+}
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 3bee4b7..b839706 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -69,7 +69,7 @@
String getUserAccount(int userId);
void setUserAccount(int userId, String accountName);
long getUserCreationTime(int userId);
- boolean isRestricted();
+ boolean isRestricted(int userId);
boolean canHaveRestrictedProfile(int userId);
int getUserSerialNumber(int userId);
int getUserHandle(int userSerialNumber);
@@ -93,10 +93,10 @@
boolean isQuietModeEnabled(int userId);
void setSeedAccountData(int userId, in String accountName,
in String accountType, in PersistableBundle accountOptions, boolean persist);
- String getSeedAccountName();
- String getSeedAccountType();
- PersistableBundle getSeedAccountOptions();
- void clearSeedAccountData();
+ String getSeedAccountName(int userId);
+ String getSeedAccountType(int userId);
+ PersistableBundle getSeedAccountOptions(int userId);
+ void clearSeedAccountData(int userId);
boolean someUserHasSeedAccount(in String accountName, in String accountType);
boolean isProfile(int userId);
boolean isManagedProfile(int userId);
@@ -118,7 +118,7 @@
boolean isUserRunning(int userId);
boolean isUserForeground(int userId);
boolean isUserNameSet(int userId);
- boolean hasRestrictedProfiles();
+ boolean hasRestrictedProfiles(int userId);
boolean requestQuietModeEnabled(String callingPackage, boolean enableQuietMode, int userId, in IntentSender target, int flags);
String getUserName();
long getUserStartRealtime();
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 5a131b3..8a80457 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;
@@ -199,7 +200,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/os/RemoteException.java b/core/java/android/os/RemoteException.java
index e9fc2f3..878e141 100644
--- a/core/java/android/os/RemoteException.java
+++ b/core/java/android/os/RemoteException.java
@@ -60,7 +60,7 @@
/**
* Rethrow this exception when we know it came from the system server. This
* gives us an opportunity to throw a nice clean
- * {@link DeadSystemException} signal to avoid spamming logs with
+ * {@link DeadSystemRuntimeException} signal to avoid spamming logs with
* misleading stack traces.
* <p>
* Apps making calls into the system server may end up persisting internal
@@ -73,7 +73,7 @@
@NonNull
public RuntimeException rethrowFromSystemServer() {
if (this instanceof DeadObjectException) {
- throw new RuntimeException(new DeadSystemException());
+ throw new DeadSystemRuntimeException();
} else {
throw new RuntimeException(this);
}
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index 46eb2ec..cba4423 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -63,7 +63,7 @@
mMemoryRegistration = new MemoryRegistration(mSize);
mCleaner = Cleaner.create(mFileDescriptor,
- new Closer(mFileDescriptor, mMemoryRegistration));
+ new Closer(mFileDescriptor.getInt$(), mMemoryRegistration));
}
/**
@@ -276,6 +276,7 @@
*/
@Override
public void close() {
+ mFileDescriptor.setInt$(-1);
if (mCleaner != null) {
mCleaner.clean();
mCleaner = null;
@@ -325,10 +326,10 @@
* Cleaner that closes the FD
*/
private static final class Closer implements Runnable {
- private FileDescriptor mFd;
+ private int mFd;
private MemoryRegistration mMemoryReference;
- private Closer(FileDescriptor fd, MemoryRegistration memoryReference) {
+ private Closer(int fd, MemoryRegistration memoryReference) {
mFd = fd;
mMemoryReference = memoryReference;
}
@@ -336,7 +337,9 @@
@Override
public void run() {
try {
- Os.close(mFd);
+ FileDescriptor fd = new FileDescriptor();
+ fd.setInt$(mFd);
+ Os.close(fd);
} catch (ErrnoException e) { /* swallow error */ }
mMemoryReference.release();
mMemoryReference = null;
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 55b1f940..07f4082 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -73,6 +73,15 @@
"[^/]*BatteryConsumer[^/]*\\.java"
],
"name": "BatteryUsageStatsProtoTests"
+ },
+ {
+ "file_patterns": ["SharedMemory[^/]*\\.java"],
+ "name": "CtsOsTestCases",
+ "options": [
+ {
+ "include-filter": "android.os.cts.SharedMemoryTest"
+ }
+ ]
}
],
"postsubmit": [
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 09b7aaa..da09375 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -37,6 +37,9 @@
import android.app.ActivityManager;
import android.app.PropertyInvalidatedCache;
import android.app.admin.DevicePolicyManager;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -55,6 +58,7 @@
import android.telephony.TelephonyManager;
import android.util.AndroidException;
import android.util.ArraySet;
+import android.util.Log;
import android.view.WindowManager.LayoutParams;
import com.android.internal.R;
@@ -1719,6 +1723,61 @@
}
}
+ /**
+ * For apps targeting {@link Build.VERSION_CODES#TIRAMISU} and above, any UserManager API marked
+ * as {@link android.annotation.UserHandleAware @UserHandleAware} will use the context user
+ * (rather than the calling user).
+ * For apps targeting an SDK version <em>below</em> this, the behaviour
+ * depends on the particular method and when it was first introduced:
+ * <ul>
+ * <li>
+ * if the {@literal @}UserHandleAware specifies a
+ * {@link android.annotation.UserHandleAware#enabledSinceTargetSdkVersion} of
+ * {@link Build.VERSION_CODES#TIRAMISU} the <em>calling</em> user is used.
+ * </li>
+ * <li>
+ * if the {@literal @}UserHandleAware doesn't specify a
+ * {@link android.annotation.UserHandleAware#enabledSinceTargetSdkVersion}, the
+ * <em>context</em> user is used.
+ * </li>
+ * <li>there should currently be no other values used by UserManager for
+ * {@link android.annotation.UserHandleAware#enabledSinceTargetSdkVersion}, since all
+ * old implicitly user-dependant APIs were updated in that version and anything
+ * introduced more recently should already be {@literal @}UserHandleAware.
+ * </li>
+ * </ul>
+ *
+ * Note that when an API marked with
+ * {@link android.annotation.UserHandleAware#enabledSinceTargetSdkVersion} is run
+ * on a device whose OS predates that version, the calling user will be used, since on such a
+ * device, the API is not {@literal @}UserHandleAware yet.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public static final long ALWAYS_USE_CONTEXT_USER = 183155436L;
+
+ /**
+ * Returns the context user or the calling user, depending on the target SDK.
+ * New APIs do not require such gating and therefore should always use mUserId instead.
+ * @see #ALWAYS_USE_CONTEXT_USER
+ */
+ private @UserIdInt int getContextUserIfAppropriate() {
+ if (CompatChanges.isChangeEnabled(ALWAYS_USE_CONTEXT_USER)) {
+ return mUserId;
+ } else {
+ final int callingUser = UserHandle.myUserId();
+ if (callingUser != mUserId) {
+ Log.w(TAG, "Using the calling user " + callingUser
+ + ", rather than the specified context user " + mUserId
+ + ", because API is only UserHandleAware on higher targetSdkVersions.",
+ new Throwable());
+ }
+ return callingUser;
+ }
+ }
+
/** @hide */
@UnsupportedAppUsage
public static UserManager get(Context context) {
@@ -1784,7 +1843,12 @@
* @hide
*/
@Deprecated
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.MANAGE_USERS}, // Can be INTERACT_ACROSS_USERS instead.
+ conditional = true)
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UserHandleAware()
public boolean canSwitchUsers() {
boolean allowUserSwitchingWhenSystemUserLocked = Settings.Global.getInt(
mContext.getContentResolver(),
@@ -1795,14 +1859,13 @@
if (telephonyManager != null) {
inCall = telephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE;
}
- boolean isUserSwitchDisallowed = hasUserRestriction(DISALLOW_USER_SWITCH);
+ boolean isUserSwitchDisallowed = hasUserRestrictionForUser(DISALLOW_USER_SWITCH, mUserId);
return (allowUserSwitchingWhenSystemUserLocked || isSystemUserUnlocked) && !inCall
&& !isUserSwitchDisallowed;
}
/**
- * Returns whether switching users is currently allowed for the user this process is running
- * under.
+ * Returns whether switching users is currently allowed for the context user.
* <p>
* Switching users is not allowed in the following cases:
* <li>the user is in a phone call</li>
@@ -1816,8 +1879,9 @@
@RequiresPermission(allOf = {Manifest.permission.READ_PHONE_STATE,
android.Manifest.permission.MANAGE_USERS,
android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public @UserSwitchabilityResult int getUserSwitchability() {
- return getUserSwitchability(Process.myUserHandle());
+ return getUserSwitchability(UserHandle.of(getContextUserIfAppropriate()));
}
/**
@@ -1842,7 +1906,7 @@
if (tm.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
flags |= SWITCHABILITY_STATUS_USER_IN_CALL;
}
- if (hasUserRestriction(DISALLOW_USER_SWITCH, userHandle)) {
+ if (hasUserRestrictionForUser(DISALLOW_USER_SWITCH, userHandle)) {
flags |= SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED;
}
@@ -1862,14 +1926,16 @@
}
/**
- * Returns the user handle for the user that this process is running under.
+ * Returns the userId for the context user.
*
- * @return the user handle of this process.
+ * @return the userId of the context user.
* @hide
*/
@UnsupportedAppUsage
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ // *** Do NOT use this in UserManager. Instead always use mUserId. ***
public @UserIdInt int getUserHandle() {
- return UserHandle.myUserId();
+ return getContextUserIfAppropriate();
}
/**
@@ -1877,9 +1943,9 @@
* @hide
*/
@TestApi
- @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
- android.Manifest.permission.CREATE_USERS
- })
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS})
@UserHandleAware
public @NonNull String getUserType() {
UserInfo userInfo = getUserInfo(mUserId);
@@ -1919,9 +1985,10 @@
@SystemApi
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.GET_ACCOUNTS_PRIVILEGED})
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public boolean isUserNameSet() {
try {
- return mService.isUserNameSet(getUserHandle());
+ return mService.isUserNameSet(getContextUserIfAppropriate());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -1939,52 +2006,60 @@
*
* @return Returns whether the user making this call is a goat.
*/
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public boolean isUserAGoat() {
if (mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.R) {
return false;
}
+ // Caution: This is NOT @UserHandleAware (because mContext is getApplicationContext and
+ // holds a different userId), but for R+ it returns false, so it doesn't matter anyway.
return mContext.getPackageManager()
.isPackageAvailable("com.coffeestainstudios.goatsimulator");
}
/**
- * Used to check if this process is running under the primary user. The primary user
+ * Used to check if the context user is the primary user. The primary user
* is the first human user on a device. This is not supported in headless system user mode.
*
- * @return whether this process is running under the primary user.
+ * @return whether the context user is the primary user.
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public boolean isPrimaryUser() {
- UserInfo user = getUserInfo(UserHandle.myUserId());
+ final UserInfo user = getUserInfo(getContextUserIfAppropriate());
return user != null && user.isPrimary();
}
/**
- * Used to check if this process is running under the system user. The system user
+ * Used to check if the context user is the system user. The system user
* is the initial user that is implicitly created on first boot and hosts most of the
* system services.
*
- * @return whether this process is running under the system user.
+ * @return whether the context user is the system user.
*/
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public boolean isSystemUser() {
- return UserHandle.myUserId() == UserHandle.USER_SYSTEM;
+ return getContextUserIfAppropriate() == UserHandle.USER_SYSTEM;
}
/**
- * Used to check if this process is running as an admin user. An admin user is allowed to
+ * Used to check if the context user is an admin user. An admin user is allowed to
* modify or configure certain settings that aren't available to non-admin users,
* create and delete additional users, etc. There can be more than one admin users.
*
- * @return whether this process is running under an admin user.
+ * @return whether the context user is an admin user.
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS})
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public boolean isAdminUser() {
- return isUserAdmin(UserHandle.myUserId());
+ return isUserAdmin(getContextUserIfAppropriate());
}
/**
@@ -2099,22 +2174,33 @@
*/
@UnsupportedAppUsage
@Deprecated
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ requiresAnyOfPermissionsIfNotCaller = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS}
+ )
public boolean isLinkedUser() {
return isRestrictedProfile();
}
/**
- * Used to check if this process is running under a restricted profile. Restricted profiles
+ * Used to check if the context user is a restricted profile. Restricted profiles
* may have a reduced number of available apps, app restrictions, and account restrictions.
*
- * @return whether this process is running under a restricted profile.
+ * @return whether the context user is a restricted profile.
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ requiresAnyOfPermissionsIfNotCaller = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS}
+ )
public boolean isRestrictedProfile() {
try {
- return mService.isRestricted();
+ return mService.isRestricted(getContextUserIfAppropriate());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2129,11 +2215,13 @@
* @hide
*/
@SystemApi
- @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
- Manifest.permission.CREATE_USERS})
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS},
+ conditional = true)
public boolean isRestrictedProfile(@NonNull UserHandle user) {
try {
- return mService.getUserInfo(user.getIdentifier()).isRestricted();
+ return mService.isRestricted(user.getIdentifier());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2156,15 +2244,16 @@
}
/**
- * Returns whether the calling user has at least one restricted profile associated with it.
- * @return
+ * Returns whether the context user has at least one restricted profile associated with it.
+ * @return whether the user has a restricted profile associated with it
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public boolean hasRestrictedProfiles() {
try {
- return mService.hasRestrictedProfiles();
+ return mService.hasRestrictedProfiles(getContextUserIfAppropriate());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2203,29 +2292,34 @@
}
/**
- * Used to check if this process is running under a guest user. A guest user may be transient.
+ * Used to check if the context user is a guest user. A guest user may be transient.
*
- * @return whether this process is running under a guest user.
+ * @return whether the context user is a guest user.
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public boolean isGuestUser() {
- UserInfo user = getUserInfo(UserHandle.myUserId());
+ UserInfo user = getUserInfo(getContextUserIfAppropriate());
return user != null && user.isGuest();
}
/**
- * Checks if the calling app is running in a demo user. When running in a demo user,
+ * Checks if the context user is a demo user. When running in a demo user,
* apps can be more helpful to the user, or explain their features in more detail.
*
- * @return whether the caller is a demo user.
+ * @return whether the context user is a demo user.
*/
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ requiresPermissionIfNotCaller = android.Manifest.permission.MANAGE_USERS
+ )
public boolean isDemoUser() {
try {
- return mService.isDemoUser(UserHandle.myUserId());
+ return mService.isDemoUser(getContextUserIfAppropriate());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -2250,14 +2344,14 @@
}
private boolean isProfile(@UserIdInt int userId) {
- if (userId == UserHandle.myUserId()) {
+ if (userId == mUserId) {
// No need for synchronization. Once it becomes non-null, it'll be non-null forever.
// Worst case we might end up calling the AIDL method multiple times but that's fine.
if (mIsProfileCached != null) {
return mIsProfileCached;
}
try {
- mIsProfileCached = mService.isProfile(userId);
+ mIsProfileCached = mService.isProfile(mUserId);
return mIsProfileCached;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
@@ -2272,22 +2366,18 @@
}
/**
- * Checks if the calling app is running in a managed profile.
+ * Checks if the context user is a managed profile.
*
- * @return whether the caller is in a managed profile.
+ * @return whether the context user is a managed profile.
*/
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}
+ )
public boolean isManagedProfile() {
- // No need for synchronization. Once it becomes non-null, it'll be non-null forever.
- // Worst case we might end up calling the AIDL method multiple times but that's fine.
- if (mIsManagedProfileCached != null) {
- return mIsManagedProfileCached;
- }
- try {
- mIsManagedProfileCached = mService.isManagedProfile(UserHandle.myUserId());
- return mIsManagedProfileCached;
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
+ return isManagedProfile(getContextUserIfAppropriate());
}
/**
@@ -2303,13 +2393,24 @@
@RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isManagedProfile(@UserIdInt int userId) {
- if (userId == UserHandle.myUserId()) {
- return isManagedProfile();
- }
- try {
- return mService.isManagedProfile(userId);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
+ if (userId == mUserId) {
+ // No need for synchronization. Once it becomes non-null, it'll be non-null forever.
+ // Worst case we might end up calling the AIDL method multiple times but that's fine.
+ if (mIsManagedProfileCached != null) {
+ return mIsManagedProfileCached;
+ }
+ try {
+ mIsManagedProfileCached = mService.isManagedProfile(mUserId);
+ return mIsManagedProfileCached;
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ } else {
+ try {
+ return mService.isManagedProfile(userId);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
}
@@ -2339,15 +2440,16 @@
}
/**
- * Checks if the calling app is running as an ephemeral user.
+ * Checks if the context user is an ephemeral user.
*
- * @return whether the caller is an ephemeral user.
+ * @return whether the context user is an ephemeral user.
* @hide
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
+ @UserHandleAware()
public boolean isEphemeralUser() {
- return isUserEphemeral(UserHandle.myUserId());
+ return isUserEphemeral(mUserId);
}
/**
@@ -2436,7 +2538,7 @@
}
/**
- * Return whether the calling user is running in an "unlocked" state.
+ * Return whether the context user is running in an "unlocked" state.
* <p>
* On devices with direct boot, a user is unlocked only after they've
* entered their credentials (such as a lock pattern or PIN). On devices
@@ -2449,8 +2551,14 @@
* @see Intent#ACTION_USER_UNLOCKED
* @see Context#createDeviceProtectedStorageContext()
*/
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}
+ )
public boolean isUserUnlocked() {
- return isUserUnlocked(Process.myUserHandle());
+ return isUserUnlocked(getContextUserIfAppropriate());
}
/**
@@ -2573,7 +2681,14 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ // NOT @UserHandleAware
public long getUserStartRealtime() {
+ if (getContextUserIfAppropriate() != UserHandle.myUserId()) {
+ // Note: If we want to support this in the future, also annotate with
+ // @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ throw new IllegalArgumentException("Calling from a context differing from the calling "
+ + "user is not currently supported.");
+ }
try {
return mService.getUserStartRealtime();
} catch (RemoteException re) {
@@ -2582,13 +2697,20 @@
}
/**
- * Return the time when the calling user was unlocked elapsed milliseconds since boot,
+ * Return the time when the context user was unlocked elapsed milliseconds since boot,
* or 0 if not unlocked.
*
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ // NOT @UserHandleAware
public long getUserUnlockRealtime() {
+ if (getContextUserIfAppropriate() != UserHandle.myUserId()) {
+ // Note: If we want to support this in the future, also annotate with
+ // @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ throw new IllegalArgumentException("Calling from a context differing from the calling "
+ + "user is not currently supported.");
+ }
try {
return mService.getUserUnlockRealtime();
} catch (RemoteException re) {
@@ -2657,11 +2779,21 @@
}
/**
- * Returns the user-wide restrictions imposed on this user.
+ * Returns the user-wide restrictions imposed on the context user.
* @return a Bundle containing all the restrictions.
*/
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}
+ )
public Bundle getUserRestrictions() {
- return getUserRestrictions(Process.myUserHandle());
+ try {
+ return mService.getUserRestrictions(getContextUserIfAppropriate());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
/**
@@ -2727,7 +2859,7 @@
}
/**
- * Sets the value of a specific restriction.
+ * Sets the value of a specific restriction on the context user.
* Requires the MANAGE_USERS permission.
* @param key the key of the restriction
* @param value the value for the restriction
@@ -2738,8 +2870,13 @@
*/
@Deprecated
@RequiresPermission(Manifest.permission.MANAGE_USERS)
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public void setUserRestriction(String key, boolean value) {
- setUserRestriction(key, value, Process.myUserHandle());
+ try {
+ mService.setUserRestriction(key, value, getContextUserIfAppropriate());
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
}
/**
@@ -2764,14 +2901,20 @@
}
/**
- * Returns whether the current user has been disallowed from performing certain actions
+ * Returns whether the context user has been disallowed from performing certain actions
* or setting certain settings.
*
* @param restrictionKey The string key representing the restriction.
- * @return {@code true} if the current user has the given restriction, {@code false} otherwise.
+ * @return {@code true} if the context user has the given restriction, {@code false} otherwise.
*/
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ requiresAnyOfPermissionsIfNotCallerProfileGroup = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}
+ )
public boolean hasUserRestriction(@UserRestrictionKey String restrictionKey) {
- return hasUserRestrictionForUser(restrictionKey, Process.myUserHandle());
+ return hasUserRestrictionForUser(restrictionKey, getContextUserIfAppropriate());
}
/**
@@ -2780,8 +2923,13 @@
* or setting certain settings.
* @param restrictionKey the string key representing the restriction
* @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+ * @deprecated Use {@link #hasUserRestrictionForUser(String, UserHandle)} instead.
*/
@UnsupportedAppUsage
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+ @Deprecated
public boolean hasUserRestriction(@UserRestrictionKey String restrictionKey,
UserHandle userHandle) {
return hasUserRestrictionForUser(restrictionKey, userHandle);
@@ -2805,8 +2953,16 @@
android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean hasUserRestrictionForUser(@NonNull @UserRestrictionKey String restrictionKey,
@NonNull UserHandle userHandle) {
+ return hasUserRestrictionForUser(restrictionKey, userHandle.getIdentifier());
+ }
+
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
+ private boolean hasUserRestrictionForUser(@NonNull @UserRestrictionKey String restrictionKey,
+ @UserIdInt int userId) {
try {
- return mService.hasUserRestriction(restrictionKey, userHandle.getIdentifier());
+ return mService.hasUserRestriction(restrictionKey, userId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -3148,7 +3304,7 @@
/**
* Creates a restricted profile with the specified name. This method also sets necessary
- * restrictions and adds shared accounts.
+ * restrictions and adds shared accounts (with the context user).
*
* @param name profile's name
* @return the {@link UserInfo} object for the created user, or {@code null} if the user
@@ -3159,16 +3315,17 @@
@TestApi
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS})
+ @UserHandleAware()
public @Nullable UserInfo createRestrictedProfile(@Nullable String name) {
try {
- UserHandle parentUserHandle = Process.myUserHandle();
- UserInfo user = mService.createRestrictedProfileWithThrow(name,
- parentUserHandle.getIdentifier());
- if (user != null) {
+ final int parentUserId = mUserId;
+ final UserInfo profile = mService.createRestrictedProfileWithThrow(name, parentUserId);
+ if (profile != null) {
+ final UserHandle parentUserHandle = UserHandle.of(parentUserId);
AccountManager.get(mContext).addSharedAccountsFromParentUser(parentUserHandle,
- UserHandle.of(user.id));
+ UserHandle.of(profile.id));
}
- return user;
+ return profile;
} catch (ServiceSpecificException e) {
return null;
} catch (RemoteException re) {
@@ -3260,13 +3417,14 @@
/**
* @hide
*
- * Returns the preferred account name for user creation.
+ * Returns the preferred account name for the context user's creation.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public String getSeedAccountName() {
try {
- return mService.getSeedAccountName();
+ return mService.getSeedAccountName(getContextUserIfAppropriate());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -3275,13 +3433,14 @@
/**
* @hide
*
- * Returns the preferred account type for user creation.
+ * Returns the preferred account type for the context user's creation.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public String getSeedAccountType() {
try {
- return mService.getSeedAccountType();
+ return mService.getSeedAccountType(getContextUserIfAppropriate());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -3291,13 +3450,14 @@
* @hide
*
* Returns the preferred account's options bundle for user creation.
- * @return Any options set by the requestor that created the user.
+ * @return Any options set by the requestor that created the context user.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public PersistableBundle getSeedAccountOptions() {
try {
- return mService.getSeedAccountOptions();
+ return mService.getSeedAccountOptions(getContextUserIfAppropriate());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -3327,13 +3487,14 @@
/**
* @hide
- * Clears the seed information used to create this user.
+ * Clears the seed information used to create the context user.
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public void clearSeedAccountData() {
try {
- mService.clearSeedAccountData();
+ mService.clearSeedAccountData(getContextUserIfAppropriate());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -3657,8 +3818,8 @@
* {@link #getEnabledProfiles(int)} if you need only the enabled ones.
* <p>Note that this includes all profile types (not including Restricted profiles).
*
- * <p>Requires {@link android.Manifest.permission#MANAGE_USERS}.
- * {@link android.Manifest.permission#CREATE_USERS} suffices if userId is the calling user.
+ * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
+ * {@link android.Manifest.permission#CREATE_USERS} if userId is not the calling user.
* @param userId profiles of this user will be returned.
* @return the list of profiles.
* @hide
@@ -3710,8 +3871,8 @@
* Note that this returns only enabled.
* <p>Note that this includes all profile types (not including Restricted profiles).
*
- * <p>Requires {@link android.Manifest.permission#MANAGE_USERS}.
- * {@link android.Manifest.permission#CREATE_USERS} suffices if userId is the calling user.
+ * <p>Requires {@link android.Manifest.permission#MANAGE_USERS} or
+ * {@link android.Manifest.permission#CREATE_USERS} if userId is not the calling user.
* @param userId profiles of this user will be returned.
* @return the list of profiles.
* @hide
@@ -3728,19 +3889,21 @@
}
/**
- * Returns a list of UserHandles for profiles associated with the user that the calling process
- * is running on, including the user itself.
+ * Returns a list of UserHandles for profiles associated with the context user, including the
+ * user itself.
* <p>Note that this includes all profile types (not including Restricted profiles).
*
- * @return A non-empty list of UserHandles associated with the calling user.
+ * @return A non-empty list of UserHandles associated with the context user.
*/
+ @UserHandleAware(
+ enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU,
+ requiresAnyOfPermissionsIfNotCaller = {
+ Manifest.permission.MANAGE_USERS,
+ Manifest.permission.CREATE_USERS}
+ )
public List<UserHandle> getUserProfiles() {
- int[] userIds = getProfileIds(UserHandle.myUserId(), true /* enabledOnly */);
- List<UserHandle> result = new ArrayList<>(userIds.length);
- for (int userId : userIds) {
- result.add(UserHandle.of(userId));
- }
- return result;
+ int[] userIds = getProfileIds(getContextUserIfAppropriate(), true /* enabledOnly */);
+ return convertUserIdsToUserHandles(userIds);
}
/**
@@ -3748,7 +3911,7 @@
* user itself.
* <p>Note that this includes all profile types (not including Restricted profiles).
*
- * @return A non-empty list of UserHandles associated with the calling user.
+ * @return A non-empty list of UserHandles associated with the context user.
* @hide
*/
@SystemApi
@@ -3764,7 +3927,7 @@
* itself.
* <p>Note that this includes all profile types (not including Restricted profiles).
*
- * @return A non-empty list of UserHandles associated with the calling user.
+ * @return A non-empty list of UserHandles associated with the context user.
* @hide
*/
@SystemApi
@@ -3781,13 +3944,18 @@
* <p>Note that this includes all profile types (not including Restricted profiles).
*
* @param enabledOnly whether to return only {@link UserInfo#isEnabled() enabled} profiles
- * @return A non-empty list of UserHandles associated with the calling user.
+ * @return A non-empty list of UserHandles associated with the context user.
*/
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.CREATE_USERS}, conditional = true)
@UserHandleAware
private @NonNull List<UserHandle> getProfiles(boolean enabledOnly) {
final int[] userIds = getProfileIds(mUserId, enabledOnly);
+ return convertUserIdsToUserHandles(userIds);
+ }
+
+ /** Converts the given array of userIds to a List of UserHandles. */
+ private @NonNull List<UserHandle> convertUserIdsToUserHandles(@NonNull int[] userIds) {
final List<UserHandle> result = new ArrayList<>(userIds.length);
for (int userId : userIds) {
result.add(UserHandle.of(userId));
@@ -4433,6 +4601,11 @@
* @return true if user switcher is enabled
* @hide
*/
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS // And INTERACT_ if diff profile group
+ })
+ @UserHandleAware()
public boolean isUserSwitcherEnabled() {
return isUserSwitcherEnabled(true);
}
@@ -4445,11 +4618,16 @@
* @return true if user switcher should be shown.
* @hide
*/
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS // And INTERACT_ if diff profile group
+ })
+ @UserHandleAware()
public boolean isUserSwitcherEnabled(boolean showEvenIfNotActionable) {
if (!supportsMultipleUsers()) {
return false;
}
- if (hasUserRestriction(DISALLOW_USER_SWITCH)) {
+ if (hasUserRestrictionForUser(DISALLOW_USER_SWITCH, mUserId)) {
return false;
}
// If Demo Mode is on, don't show user switcher
@@ -4468,10 +4646,14 @@
// The feature is enabled. But is it worth showing?
return showEvenIfNotActionable
|| areThereUsersToWhichToSwitch() // There are switchable users.
- || !hasUserRestriction(UserManager.DISALLOW_ADD_USER); // New users can be added.
+ || !hasUserRestrictionForUser(DISALLOW_ADD_USER, mUserId); // New users can be added
}
/** Returns whether there are any users (other than the current user) to which to switch. */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS
+ })
private boolean areThereUsersToWhichToSwitch() {
final List<UserInfo> users = getAliveUsers();
if (users == null) {
@@ -4530,8 +4712,8 @@
}
/**
- * Returns a {@link Bundle} containing any saved application restrictions for this user, for the
- * given package name. Only an application with this package name can call this method.
+ * Returns a {@link Bundle} containing any saved application restrictions for the context user,
+ * for the given package name. Only an application with this package name can call this method.
*
* <p>The returned {@link Bundle} consists of key-value pairs, as defined by the application,
* where the types of values may be:
@@ -4551,9 +4733,11 @@
* @see #KEY_RESTRICTIONS_PENDING
*/
@WorkerThread
+ @UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public Bundle getApplicationRestrictions(String packageName) {
try {
- return mService.getApplicationRestrictions(packageName);
+ return mService.getApplicationRestrictionsForUser(packageName,
+ getContextUserIfAppropriate());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 570d73d..d120264 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -6159,9 +6159,10 @@
*
* @hide
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @SystemApi
@TestApi
- public static final Uri ENTERPRISE_CONTENT_URI =
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+ public static final @NonNull Uri ENTERPRISE_CONTENT_URI =
Uri.withAppendedPath(Data.ENTERPRISE_CONTENT_URI, "phones");
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2d10ade..cbd405e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1919,7 +1919,6 @@
/**
* Activity Action: Show app listing settings, filtered by those that send notifications.
*
- * @hide
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_ALL_APPS_NOTIFICATION_SETTINGS =
diff --git a/core/java/android/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl
index c142a53..59f1e8e 100644
--- a/core/java/android/service/voice/IVoiceInteractionSession.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl
@@ -22,6 +22,7 @@
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.IBinder;
+import android.service.voice.VisibleActivityInfo;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
@@ -39,4 +40,5 @@
void closeSystemDialogs();
void onLockscreenShown();
void destroy();
+ void updateVisibleActivityInfo(in VisibleActivityInfo visibleActivityInfo, int type);
}
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/package-info.java b/core/java/android/service/voice/VisibleActivityInfo.aidl
similarity index 81%
rename from apex/appsearch/testing/java/com/android/server/appsearch/testing/package-info.java
rename to core/java/android/service/voice/VisibleActivityInfo.aidl
index 5c919b4..34bd57c 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/package-info.java
+++ b/core/java/android/service/voice/VisibleActivityInfo.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-/**
- * @hide
- */
-package com.android.server.appsearch.testing;
+package android.service.voice;
+
+parcelable VisibleActivityInfo;
diff --git a/core/java/android/service/voice/VisibleActivityInfo.java b/core/java/android/service/voice/VisibleActivityInfo.java
new file mode 100644
index 0000000..139544c
--- /dev/null
+++ b/core/java/android/service/voice/VisibleActivityInfo.java
@@ -0,0 +1,205 @@
+/*
+ * 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.service.voice;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * The class is used to represent a visible activity information. The system provides this to
+ * services that need to know {@link android.service.voice.VoiceInteractionSession.ActivityId}.
+ */
+@DataClass(
+ genConstructor = false,
+ genEqualsHashCode = true,
+ genHiddenConstDefs = false,
+ genGetters = false,
+ genToString = true
+)
+public final class VisibleActivityInfo implements Parcelable {
+
+ /**
+ * Indicates that it is a new visible activity.
+ *
+ * @hide
+ */
+ public static final int TYPE_ACTIVITY_ADDED = 1;
+
+ /**
+ * Indicates that it has become a invisible activity.
+ *
+ * @hide
+ */
+ public static final int TYPE_ACTIVITY_REMOVED = 2;
+
+ /**
+ * The identifier of the task this activity is in.
+ */
+ private final int mTaskId;
+
+ /**
+ * Token for targeting this activity for assist purposes.
+ */
+ @NonNull
+ private final IBinder mAssistToken;
+
+ /** @hide */
+ @TestApi
+ public VisibleActivityInfo(
+ int taskId,
+ @NonNull IBinder assistToken) {
+ Objects.requireNonNull(assistToken);
+ mTaskId = taskId;
+ mAssistToken = assistToken;
+ }
+
+ /**
+ * Returns the {@link android.service.voice.VoiceInteractionSession.ActivityId} of this
+ * visible activity which can be used to interact with an activity, for example through
+ * {@link VoiceInteractionSession#requestDirectActions(VoiceInteractionSession.ActivityId,
+ * CancellationSignal, Executor, Consumer)}.
+ */
+ public @NonNull VoiceInteractionSession.ActivityId getActivityId() {
+ return new VoiceInteractionSession.ActivityId(mTaskId, mAssistToken);
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/service/voice/VisibleActivityInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "VisibleActivityInfo { " +
+ "taskId = " + mTaskId + ", " +
+ "assistToken = " + mAssistToken +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(VisibleActivityInfo other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ VisibleActivityInfo that = (VisibleActivityInfo) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mTaskId == that.mTaskId
+ && Objects.equals(mAssistToken, that.mAssistToken);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mTaskId;
+ _hash = 31 * _hash + Objects.hashCode(mAssistToken);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mTaskId);
+ dest.writeStrongBinder(mAssistToken);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ VisibleActivityInfo(@NonNull Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int taskId = in.readInt();
+ IBinder assistToken = (IBinder) in.readStrongBinder();
+
+ this.mTaskId = taskId;
+ this.mAssistToken = assistToken;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mAssistToken);
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<VisibleActivityInfo> CREATOR
+ = new Parcelable.Creator<VisibleActivityInfo>() {
+ @Override
+ public VisibleActivityInfo[] newArray(int size) {
+ return new VisibleActivityInfo[size];
+ }
+
+ @Override
+ public VisibleActivityInfo createFromParcel(@NonNull Parcel in) {
+ return new VisibleActivityInfo(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1632383555284L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/service/voice/VisibleActivityInfo.java",
+ inputSignatures = "public static final int TYPE_ACTIVITY_ADDED\npublic static final int TYPE_ACTIVITY_REMOVED\nprivate final int mTaskId\nprivate final @android.annotation.NonNull android.os.IBinder mAssistToken\npublic @android.annotation.NonNull android.service.voice.VoiceInteractionSession.ActivityId getActivityId()\nclass VisibleActivityInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genEqualsHashCode=true, genHiddenConstDefs=false, genGetters=false, genToString=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/service/voice/VoiceInteractionManagerInternal.java b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
index c048286..c806409 100644
--- a/core/java/android/service/voice/VoiceInteractionManagerInternal.java
+++ b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
@@ -22,7 +22,6 @@
import com.android.internal.annotations.Immutable;
-
/**
* @hide
* Private interface to the VoiceInteractionManagerService for use by ActivityManagerService.
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index d46265e..30e4a23 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -73,6 +73,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -177,6 +178,10 @@
ICancellationSignal mKillCallback;
+ private final Map<VisibleActivityCallback, Executor> mVisibleActivityCallbacks =
+ new ArrayMap<>();
+ private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>();
+
final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
@Override
public IVoiceInteractorRequest startConfirmation(String callingPackage,
@@ -352,6 +357,13 @@
public void destroy() {
mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY));
}
+
+ @Override
+ public void updateVisibleActivityInfo(VisibleActivityInfo visibleActivityInfo, int type) {
+ mHandlerCaller.sendMessage(
+ mHandlerCaller.obtainMessageIO(MSG_UPDATE_VISIBLE_ACTIVITY_INFO, type,
+ visibleActivityInfo));
+ }
};
/**
@@ -843,6 +855,9 @@
static final int MSG_SHOW = 106;
static final int MSG_HIDE = 107;
static final int MSG_ON_LOCKSCREEN_SHOWN = 108;
+ static final int MSG_UPDATE_VISIBLE_ACTIVITY_INFO = 109;
+ static final int MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK = 110;
+ static final int MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK = 111;
class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback {
@Override
@@ -928,6 +943,27 @@
if (DEBUG) Log.d(TAG, "onLockscreenShown");
onLockscreenShown();
break;
+ case MSG_UPDATE_VISIBLE_ACTIVITY_INFO:
+ if (DEBUG) {
+ Log.d(TAG, "doUpdateVisibleActivityInfo: visibleActivityInfo=" + msg.obj
+ + " type=" + msg.arg1);
+ }
+ doUpdateVisibleActivityInfo((VisibleActivityInfo) msg.obj, msg.arg1);
+ break;
+ case MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK:
+ if (DEBUG) {
+ Log.d(TAG, "doRegisterVisibleActivityCallback");
+ }
+ args = (SomeArgs) msg.obj;
+ doRegisterVisibleActivityCallback((Executor) args.arg1,
+ (VisibleActivityCallback) args.arg2);
+ break;
+ case MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK:
+ if (DEBUG) {
+ Log.d(TAG, "doUnregisterVisibleActivityCallback");
+ }
+ doUnregisterVisibleActivityCallback((VisibleActivityCallback) msg.obj);
+ break;
}
if (args != null) {
args.recycle();
@@ -1122,6 +1158,86 @@
}
}
+ private void doUpdateVisibleActivityInfo(VisibleActivityInfo visibleActivityInfo, int type) {
+
+ if (mVisibleActivityCallbacks.isEmpty()) {
+ return;
+ }
+
+ switch (type) {
+ case VisibleActivityInfo.TYPE_ACTIVITY_ADDED:
+ informVisibleActivityChanged(visibleActivityInfo, type);
+ mVisibleActivityInfos.add(visibleActivityInfo);
+ break;
+ case VisibleActivityInfo.TYPE_ACTIVITY_REMOVED:
+ informVisibleActivityChanged(visibleActivityInfo, type);
+ mVisibleActivityInfos.remove(visibleActivityInfo);
+ break;
+ }
+ }
+
+ private void doRegisterVisibleActivityCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull VisibleActivityCallback callback) {
+ if (mVisibleActivityCallbacks.containsKey(callback)) {
+ if (DEBUG) {
+ Log.d(TAG, "doRegisterVisibleActivityCallback: callback has registered");
+ }
+ return;
+ }
+
+ int preCallbackCount = mVisibleActivityCallbacks.size();
+ mVisibleActivityCallbacks.put(callback, executor);
+
+ if (preCallbackCount == 0) {
+ try {
+ mSystemService.startListeningVisibleActivityChanged(mToken);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ } else {
+ for (int i = 0; i < mVisibleActivityInfos.size(); i++) {
+ final VisibleActivityInfo visibleActivityInfo = mVisibleActivityInfos.get(i);
+ executor.execute(() -> callback.onVisible(visibleActivityInfo));
+ }
+ }
+ }
+
+ private void doUnregisterVisibleActivityCallback(@NonNull VisibleActivityCallback callback) {
+ mVisibleActivityCallbacks.remove(callback);
+
+ if (mVisibleActivityCallbacks.size() == 0) {
+ mVisibleActivityInfos.clear();
+ try {
+ mSystemService.stopListeningVisibleActivityChanged(mToken);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private void informVisibleActivityChanged(VisibleActivityInfo visibleActivityInfo, int type) {
+ for (Map.Entry<VisibleActivityCallback, Executor> e :
+ mVisibleActivityCallbacks.entrySet()) {
+ final Executor executor = e.getValue();
+ final VisibleActivityCallback visibleActivityCallback = e.getKey();
+
+ switch (type) {
+ case VisibleActivityInfo.TYPE_ACTIVITY_ADDED:
+ Binder.withCleanCallingIdentity(() -> {
+ executor.execute(
+ () -> visibleActivityCallback.onVisible(visibleActivityInfo));
+ });
+ break;
+ case VisibleActivityInfo.TYPE_ACTIVITY_REMOVED:
+ Binder.withCleanCallingIdentity(() -> {
+ executor.execute(() -> visibleActivityCallback.onInvisible(
+ visibleActivityInfo.getActivityId()));
+ });
+ break;
+ }
+ }
+ }
+
void ensureWindowCreated() {
if (mInitialized) {
return;
@@ -1926,6 +2042,45 @@
}
/**
+ * Registers a callback that will be notified when visible activities have been changed.
+ *
+ * @param executor The handler to receive the callback.
+ * @param callback The callback to receive the response.
+ *
+ * @throws IllegalStateException if calling this method before onCreate().
+ */
+ public final void registerVisibleActivityCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull VisibleActivityCallback callback) {
+ if (DEBUG) {
+ Log.d(TAG, "registerVisibleActivityCallback");
+ }
+ if (mToken == null) {
+ throw new IllegalStateException("Can't call before onCreate()");
+ }
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ mHandlerCaller.sendMessage(
+ mHandlerCaller.obtainMessageOO(MSG_REGISTER_VISIBLE_ACTIVITY_CALLBACK, executor,
+ callback));
+ }
+
+ /**
+ * Unregisters the callback.
+ *
+ * @param callback The callback to receive the response.
+ */
+ public final void unregisterVisibleActivityCallback(@NonNull VisibleActivityCallback callback) {
+ if (DEBUG) {
+ Log.d(TAG, "unregisterVisibleActivityCallback");
+ }
+ Objects.requireNonNull(callback);
+
+ mHandlerCaller.sendMessage(
+ mHandlerCaller.obtainMessageO(MSG_UNREGISTER_VISIBLE_ACTIVITY_CALLBACK, callback));
+ }
+
+ /**
* Print the Service's state into the given stream. This gets invoked by
* {@link VoiceInteractionSessionService} when its Service
* {@link android.app.Service#dump} method is called.
@@ -1975,6 +2130,17 @@
}
/**
+ * Callback interface for receiving visible activity changes used for assistant usage.
+ */
+ public interface VisibleActivityCallback {
+ /** Callback to inform that an activity has become visible. */
+ default void onVisible(@NonNull VisibleActivityInfo activityInfo) {}
+
+ /** Callback to inform that a visible activity has gone. */
+ default void onInvisible(@NonNull ActivityId activityId) {}
+ }
+
+ /**
* Represents assist state captured when this session was started.
* It contains the various assist data objects and a reference to
* the source activity.
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 5153aee..c0bc991 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -648,7 +648,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/java/android/util/MathUtils.java b/core/java/android/util/MathUtils.java
index 971e161..aecde44 100644
--- a/core/java/android/util/MathUtils.java
+++ b/core/java/android/util/MathUtils.java
@@ -165,6 +165,10 @@
return start + (stop - start) * amount;
}
+ public static float lerp(int start, int stop, float amount) {
+ return lerp((float) start, (float) stop, amount);
+ }
+
/**
* Returns the interpolation scalar (s) that satisfies the equation: {@code value = }{@link
* #lerp}{@code (a, b, s)}
diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java
index 5fcb011..99141c3 100644
--- a/core/java/android/view/ScrollCaptureConnection.java
+++ b/core/java/android/view/ScrollCaptureConnection.java
@@ -33,8 +33,8 @@
import com.android.internal.annotations.VisibleForTesting;
import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
/**
@@ -231,31 +231,24 @@
private static class SafeCallback<T> {
private final CancellationSignal mSignal;
- private final WeakReference<T> mTargetRef;
private final Executor mExecutor;
- private boolean mExecuted;
+ private final AtomicReference<T> mValue;
- protected SafeCallback(CancellationSignal signal, Executor executor, T target) {
+ protected SafeCallback(CancellationSignal signal, Executor executor, T value) {
mSignal = signal;
- mTargetRef = new WeakReference<>(target);
+ mValue = new AtomicReference<T>(value);
mExecutor = executor;
}
- // Provide the target to the consumer to invoke, forward on handler thread ONCE,
- // and only if noy cancelled, and the target is still available (not collected)
- protected final void maybeAccept(Consumer<T> targetConsumer) {
- if (mExecuted) {
- return;
- }
- mExecuted = true;
+ // Provide the value to the consumer to accept only once.
+ protected final void maybeAccept(Consumer<T> consumer) {
+ T value = mValue.getAndSet(null);
if (mSignal.isCanceled()) {
return;
}
- T target = mTargetRef.get();
- if (target == null) {
- return;
+ if (value != null) {
+ mExecutor.execute(() -> consumer.accept(value));
}
- mExecutor.execute(() -> targetConsumer.accept(target));
}
static Runnable create(CancellationSignal signal, Executor executor, Runnable target) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 96a2449..6420236 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -20906,13 +20906,14 @@
mPrivateFlags &= ~PFLAG_SCROLL_CONTAINER_ADDED;
}
+ notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
+
mAttachInfo = null;
if (mOverlay != null) {
mOverlay.getOverlayView().dispatchDetachedFromWindow();
}
notifyEnterOrExitForAutoFillIfNeeded(false);
- notifyAppearedOrDisappearedForContentCaptureIfNeeded(false);
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9710be5..9af0a02 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2831,8 +2831,13 @@
}
}
+ final boolean surfaceControlChanged =
+ (relayoutResult & RELAYOUT_RES_SURFACE_CHANGED)
+ == RELAYOUT_RES_SURFACE_CHANGED;
+
if (mSurfaceControl.isValid()) {
- updateOpacity(mWindowAttributes, dragResizing);
+ updateOpacity(mWindowAttributes, dragResizing,
+ surfaceControlChanged /*forceUpdate */);
}
if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
@@ -2867,9 +2872,7 @@
// RELAYOUT_RES_SURFACE_CHANGED since it should indicate that WMS created a new
// SurfaceControl.
surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId()
- || (relayoutResult & RELAYOUT_RES_SURFACE_CHANGED)
- == RELAYOUT_RES_SURFACE_CHANGED)
- && mSurface.isValid();
+ || surfaceControlChanged) && mSurface.isValid();
if (surfaceReplaced) {
mSurfaceSequenceId++;
}
@@ -7891,7 +7894,8 @@
return relayoutResult;
}
- private void updateOpacity(WindowManager.LayoutParams params, boolean dragResizing) {
+ private void updateOpacity(WindowManager.LayoutParams params, boolean dragResizing,
+ boolean forceUpdate) {
boolean opaque = false;
if (!PixelFormat.formatHasAlpha(params.format)
@@ -7907,7 +7911,7 @@
opaque = true;
}
- if (mIsSurfaceOpaque == opaque) {
+ if (!forceUpdate && mIsSurfaceOpaque == opaque) {
return;
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 587a270..e9dec12 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -806,6 +806,7 @@
private CharSequence mContentDescription;
private CharSequence mTooltipText;
private String mViewIdResourceName;
+ private String mUniqueId;
private ArrayList<String> mExtraDataKeys;
@UnsupportedAppUsage
@@ -3463,6 +3464,28 @@
}
/**
+ * Sets the unique id to act as a key to identify the node. If the node instance is replaced
+ * after refreshing the layout, calling this API to assign the same unique id to the new
+ * alike node can help accessibility service to identify it.
+ *
+ * @param uniqueId The unique id that is associated with a visible node on the screen
+ */
+ public void setUniqueId(@Nullable String uniqueId) {
+ enforceNotSealed();
+ mUniqueId = uniqueId;
+ }
+
+ /**
+ * Gets the unique id of the node.
+ *
+ * @return The unique id
+ */
+ @Nullable
+ public String getUniqueId() {
+ return mUniqueId;
+ }
+
+ /**
* Sets the token and node id of the leashed parent.
*
* @param token The token.
@@ -3764,6 +3787,10 @@
nonDefaultFields |= bitAt(fieldIndex);
}
fieldIndex++;
+ if (!Objects.equals(mUniqueId, DEFAULT.mUniqueId)) {
+ nonDefaultFields |= bitAt(fieldIndex);
+ }
+ fieldIndex++;
if (mTextSelectionStart != DEFAULT.mTextSelectionStart) {
nonDefaultFields |= bitAt(fieldIndex);
}
@@ -3903,6 +3930,7 @@
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mTooltipText);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeString(mViewIdResourceName);
+ if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeString(mUniqueId);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mTextSelectionStart);
if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mTextSelectionEnd);
@@ -3982,6 +4010,7 @@
mTraversalAfter = other.mTraversalAfter;
mWindowId = other.mWindowId;
mConnectionId = other.mConnectionId;
+ mUniqueId = other.mUniqueId;
mBoundsInParent.set(other.mBoundsInParent);
mBoundsInScreen.set(other.mBoundsInScreen);
mPackageName = other.mPackageName;
@@ -4154,6 +4183,7 @@
if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence();
if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString();
+ if (isBitSet(nonDefaultFields, fieldIndex++)) mUniqueId = parcel.readString();
if (isBitSet(nonDefaultFields, fieldIndex++)) mTextSelectionStart = parcel.readInt();
if (isBitSet(nonDefaultFields, fieldIndex++)) mTextSelectionEnd = parcel.readInt();
@@ -4480,6 +4510,7 @@
builder.append("; contentDescription: ").append(mContentDescription);
builder.append("; tooltipText: ").append(mTooltipText);
builder.append("; viewIdResName: ").append(mViewIdResourceName);
+ builder.append("; uniqueId: ").append(mUniqueId);
builder.append("; checkable: ").append(isCheckable());
builder.append("; checked: ").append(isChecked());
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index c8a4425..9985262 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -272,4 +272,14 @@
void triggerHardwareRecognitionEventForTest(
in SoundTrigger.KeyphraseRecognitionEvent event,
in IHotwordRecognitionStatusCallback callback);
+
+ /**
+ * Starts to listen the status of visible activity.
+ */
+ void startListeningVisibleActivityChanged(in IBinder token);
+
+ /**
+ * Stops to listen the status of visible activity.
+ */
+ void stopListeningVisibleActivityChanged(in IBinder token);
}
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index aae6f50..f8eb95c 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -59,6 +59,7 @@
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION;
import android.annotation.IntDef;
@@ -171,6 +172,7 @@
public static final int CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP = 34;
public static final int CUJ_PIP_TRANSITION = 35;
public static final int CUJ_WALLPAPER_TRANSITION = 36;
+ public static final int CUJ_USER_SWITCH = 37;
private static final int NO_STATSD_LOGGING = -1;
@@ -216,6 +218,7 @@
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PIP_TRANSITION,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__WALLPAPER_TRANSITION,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_SWITCH,
};
private static volatile InteractionJankMonitor sInstance;
@@ -272,6 +275,7 @@
CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
CUJ_PIP_TRANSITION,
CUJ_WALLPAPER_TRANSITION,
+ CUJ_USER_SWITCH,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -642,6 +646,8 @@
return "PIP_TRANSITION";
case CUJ_WALLPAPER_TRANSITION:
return "WALLPAPER_TRANSITION";
+ case CUJ_USER_SWITCH:
+ return "USER_SWITCH";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/os/ProcLocksReader.java b/core/java/com/android/internal/os/ProcLocksReader.java
index bd3115fc5..2143bc1 100644
--- a/core/java/com/android/internal/os/ProcLocksReader.java
+++ b/core/java/com/android/internal/os/ProcLocksReader.java
@@ -18,8 +18,6 @@
import com.android.internal.util.ProcFileReader;
-import libcore.io.IoUtils;
-
import java.io.FileInputStream;
import java.io.IOException;
@@ -36,6 +34,7 @@
*/
public class ProcLocksReader {
private final String mPath;
+ private ProcFileReader mReader = null;
public ProcLocksReader() {
mPath = "/proc/locks";
@@ -46,44 +45,54 @@
}
/**
- * Checks if a process corresponding to a specific pid owns any file locks.
- * @param pid The process ID for which we want to know the existence of file locks.
- * @return true If the process holds any file locks, false otherwise.
- * @throws IOException if /proc/locks can't be accessed.
+ * This interface is for AMS to run callback function on every processes one by one
+ * that hold file locks blocking other processes.
*/
- public boolean hasFileLocks(int pid) throws Exception {
- ProcFileReader reader = null;
+ public interface ProcLocksReaderCallback {
+ /**
+ * Call the callback function of handleBlockingFileLocks().
+ * @param pid Each process that hold file locks blocking other processes.
+ */
+ void onBlockingFileLock(int pid);
+ }
+
+ /**
+ * Checks if a process corresponding to a specific pid owns any file locks.
+ * @param callback Callback function, accepting pid as the input parameter.
+ * @throws IOException if /proc/locks can't be accessed or correctly parsed.
+ */
+ public void handleBlockingFileLocks(ProcLocksReaderCallback callback) throws IOException {
long last = -1;
long id; // ordinal position of the lock in the list
- int owner; // the PID of the process that owns the lock
+ int owner = -1; // the PID of the process that owns the lock
+ int pid = -1; // the PID of the process blocking others
- try {
- reader = new ProcFileReader(new FileInputStream(mPath));
-
- while (reader.hasMoreData()) {
- id = reader.nextLong(true); // lock id
- if (id == last) {
- reader.finishLine(); // blocked lock
- continue;
- }
-
- reader.nextIgnored(); // lock type: POSIX?
- reader.nextIgnored(); // lock type: MANDATORY?
- reader.nextIgnored(); // lock type: RW?
-
- owner = reader.nextInt(); // pid
- if (owner == pid) {
- return true;
- }
- reader.finishLine();
- last = id;
- }
- } catch (IOException e) {
- // TODO: let ProcFileReader log the failed line
- throw new Exception("Exception parsing /proc/locks");
- } finally {
- IoUtils.closeQuietly(reader);
+ if (mReader == null) {
+ mReader = new ProcFileReader(new FileInputStream(mPath));
+ } else {
+ mReader.rewind();
}
- return false;
+
+ while (mReader.hasMoreData()) {
+ id = mReader.nextLong(true); // lock id
+ if (id == last) {
+ mReader.finishLine(); // blocked lock
+ if (pid < 0) {
+ pid = owner; // get pid from the previous line
+ callback.onBlockingFileLock(pid);
+ }
+ continue;
+ } else {
+ pid = -1; // a new lock
+ }
+
+ mReader.nextIgnored(); // lock type: POSIX?
+ mReader.nextIgnored(); // lock type: MANDATORY?
+ mReader.nextIgnored(); // lock type: RW?
+
+ owner = mReader.nextInt(); // pid
+ mReader.finishLine();
+ last = id;
+ }
}
}
diff --git a/core/java/com/android/internal/util/ProcFileReader.java b/core/java/com/android/internal/util/ProcFileReader.java
index 0dd8ad8..b726d5d 100644
--- a/core/java/com/android/internal/util/ProcFileReader.java
+++ b/core/java/com/android/internal/util/ProcFileReader.java
@@ -17,6 +17,7 @@
package com.android.internal.util;
import java.io.Closeable;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ProtocolException;
@@ -47,6 +48,9 @@
public ProcFileReader(InputStream stream, int bufferSize) throws IOException {
mStream = stream;
mBuffer = new byte[bufferSize];
+ if (stream.markSupported()) {
+ mStream.mark(0);
+ }
// read enough to answer hasMoreData
fillBuf();
@@ -257,6 +261,24 @@
}
}
+ /**
+ * Reset file position and internal buffer
+ * @throws IOException
+ */
+ public void rewind() throws IOException {
+ if (mStream instanceof FileInputStream) {
+ ((FileInputStream) mStream).getChannel().position(0);
+ } else if (mStream.markSupported()) {
+ mStream.reset();
+ } else {
+ throw new IOException("The InputStream is NOT markable");
+ }
+
+ mTail = 0;
+ mLineFinished = false;
+ fillBuf();
+ }
+
@Override
public void close() throws IOException {
mStream.close();
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index d7eef0d..ec3dfad 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -108,17 +108,74 @@
public final String name;
public final String filename;
public final String[] dependencies;
+
+ /**
+ * SDK version this library was added to the BOOTCLASSPATH.
+ *
+ * <p>At the SDK level specified in this field and higher, the apps' uses-library tags for
+ * this library will be ignored, since the library is always available on BOOTCLASSPATH.
+ *
+ * <p>0 means not specified.
+ */
+ public final int onBootclasspathSince;
+
+ /**
+ * SDK version this library was removed from the BOOTCLASSPATH.
+ *
+ * <p>At the SDK level specified in this field and higher, this library needs to be
+ * explicitly added by apps. For compatibility reasons, when an app
+ * targets an SDK less than the value of this attribute, this library is automatically
+ * added.
+ *
+ * <p>0 means not specified.
+ */
+ public final int onBootclasspathBefore;
+
+ /**
+ * Declares whether this library can be safely ignored from <uses-library> tags.
+ *
+ * <p> This can happen if the library initially had to be explicitly depended-on using that
+ * tag but has since been moved to the BOOTCLASSPATH which means now is always available
+ * and the tag is no longer required.
+ */
+ public final boolean canBeSafelyIgnored;
+
public final boolean isNative;
- SharedLibraryEntry(String name, String filename, String[] dependencies) {
- this(name, filename, dependencies, false /* isNative */);
+
+ @VisibleForTesting
+ public SharedLibraryEntry(String name, String filename, String[] dependencies,
+ boolean isNative) {
+ this(name, filename, dependencies, 0 /* onBootclasspathSince */,
+ 0 /* onBootclasspathBefore */, isNative);
}
- SharedLibraryEntry(String name, String filename, String[] dependencies, boolean isNative) {
+ @VisibleForTesting
+ public SharedLibraryEntry(String name, String filename, String[] dependencies,
+ int onBootclasspathSince, int onBootclassPathBefore) {
+ this(name, filename, dependencies, onBootclasspathSince, onBootclassPathBefore,
+ false /* isNative */);
+ }
+
+ SharedLibraryEntry(String name, String filename, String[] dependencies,
+ int onBootclasspathSince, int onBootclasspathBefore, boolean isNative) {
this.name = name;
this.filename = filename;
this.dependencies = dependencies;
+ this.onBootclasspathSince = onBootclasspathSince;
+ this.onBootclasspathBefore = onBootclasspathBefore;
this.isNative = isNative;
+
+ canBeSafelyIgnored = this.onBootclasspathSince != 0
+ && isSdkAtLeast(this.onBootclasspathSince);
+ }
+
+ private static boolean isSdkAtLeast(int level) {
+ if ("REL".equals(Build.VERSION.CODENAME)) {
+ return Build.VERSION.SDK_INT >= level;
+ }
+ return level == Build.VERSION_CODES.CUR_DEVELOPMENT
+ || Build.VERSION.SDK_INT >= level;
}
}
@@ -771,11 +828,17 @@
XmlUtils.skipCurrentTag(parser);
}
} break;
+ case "updatable-library":
+ // "updatable-library" is meant to behave exactly like "library"
case "library": {
if (allowLibs) {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
String ldependency = parser.getAttributeValue(null, "dependency");
+ int minDeviceSdk = XmlUtils.readIntAttribute(parser, "min-device-sdk",
+ 0);
+ int maxDeviceSdk = XmlUtils.readIntAttribute(parser, "max-device-sdk",
+ 0);
if (lname == null) {
Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+ parser.getPositionDescription());
@@ -783,10 +846,20 @@
Slog.w(TAG, "<" + name + "> without file in " + permFile + " at "
+ parser.getPositionDescription());
} else {
- //Log.i(TAG, "Got library " + lname + " in " + lfile);
- SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
- ldependency == null ? new String[0] : ldependency.split(":"));
- mSharedLibraries.put(lname, entry);
+ boolean allowedMinSdk = minDeviceSdk <= Build.VERSION.SDK_INT;
+ boolean allowedMaxSdk =
+ maxDeviceSdk == 0 || maxDeviceSdk >= Build.VERSION.SDK_INT;
+ if (allowedMinSdk && allowedMaxSdk) {
+ int bcpSince = XmlUtils.readIntAttribute(parser,
+ "on-bootclasspath-since", 0);
+ int bcpBefore = XmlUtils.readIntAttribute(parser,
+ "on-bootclasspath-before", 0);
+ SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
+ ldependency == null
+ ? new String[0] : ldependency.split(":"),
+ bcpSince, bcpBefore);
+ mSharedLibraries.put(lname, entry);
+ }
}
} else {
logNotAllowedInPartition(name, permFile, parser);
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index efd0547..292f305 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -265,49 +265,9 @@
}
static void pointerCoordsFromNative(JNIEnv* env, const PointerCoords* rawPointerCoords,
- ui::Transform transform, jobject outPointerCoordsObj) {
- float rawX = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_X);
- float rawY = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_Y);
- vec2 transformed = transform.transform(rawX, rawY);
-
- float rawRelX = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- float rawRelY = rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- // Apply only rotation and scale, not translation.
- const vec2 transformedOrigin = transform.transform(0, 0);
- const vec2 transformedRel = transform.transform(rawRelX, rawRelY) - transformedOrigin;
-
- env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.x, transformed.x);
- env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.y, transformed.y);
- env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.pressure,
- rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
- env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.size,
- rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_SIZE));
- env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.touchMajor,
- rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR));
- env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.touchMinor,
- rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR));
- env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.toolMajor,
- rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR));
- env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.toolMinor,
- rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR));
- env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.orientation,
- rawPointerCoords->getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
- env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.relativeX, transformedRel.x);
- env->SetFloatField(outPointerCoordsObj, gPointerCoordsClassInfo.relativeY, transformedRel.y);
-
+ const BitSet64& axesBitsToCopy, jobject outPointerCoordsObj) {
+ BitSet64 bits = axesBitsToCopy;
uint64_t outBits = 0;
- BitSet64 bits = BitSet64(rawPointerCoords->bits);
- bits.clearBit(AMOTION_EVENT_AXIS_X);
- bits.clearBit(AMOTION_EVENT_AXIS_Y);
- bits.clearBit(AMOTION_EVENT_AXIS_PRESSURE);
- bits.clearBit(AMOTION_EVENT_AXIS_SIZE);
- bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
- bits.clearBit(AMOTION_EVENT_AXIS_TOUCH_MINOR);
- bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MAJOR);
- bits.clearBit(AMOTION_EVENT_AXIS_TOOL_MINOR);
- bits.clearBit(AMOTION_EVENT_AXIS_ORIENTATION);
- bits.clearBit(AMOTION_EVENT_AXIS_RELATIVE_X);
- bits.clearBit(AMOTION_EVENT_AXIS_RELATIVE_Y);
if (!bits.isEmpty()) {
uint32_t packedAxesCount = bits.count();
jfloatArray outValuesArray = obtainPackedAxisValuesArray(env, packedAxesCount,
@@ -444,18 +404,42 @@
|| !validatePointerCoords(env, outPointerCoordsObj)) {
return;
}
-
- const PointerCoords* rawPointerCoords;
- if (historyPos == HISTORY_CURRENT) {
- rawPointerCoords = event->getRawPointerCoords(pointerIndex);
- } else {
- size_t historySize = event->getHistorySize();
- if (!validateHistoryPos(env, historyPos, historySize)) {
- return;
- }
- rawPointerCoords = event->getHistoricalRawPointerCoords(pointerIndex, historyPos);
+ if (historyPos != HISTORY_CURRENT &&
+ !validateHistoryPos(env, historyPos, event->getHistorySize())) {
+ return;
}
- pointerCoordsFromNative(env, rawPointerCoords, event->getTransform(), outPointerCoordsObj);
+
+ // Obtain the following axis values directly from the MotionEvent instead of from the raw
+ // PointerCoords.
+ const static std::array<std::pair<int32_t /*axis*/, jfieldID>, 11> kAxesFromMotionEvent = {{
+ {AMOTION_EVENT_AXIS_X, gPointerCoordsClassInfo.x},
+ {AMOTION_EVENT_AXIS_Y, gPointerCoordsClassInfo.y},
+ {AMOTION_EVENT_AXIS_PRESSURE, gPointerCoordsClassInfo.pressure},
+ {AMOTION_EVENT_AXIS_SIZE, gPointerCoordsClassInfo.size},
+ {AMOTION_EVENT_AXIS_TOUCH_MAJOR, gPointerCoordsClassInfo.touchMajor},
+ {AMOTION_EVENT_AXIS_TOUCH_MINOR, gPointerCoordsClassInfo.touchMinor},
+ {AMOTION_EVENT_AXIS_TOOL_MAJOR, gPointerCoordsClassInfo.toolMajor},
+ {AMOTION_EVENT_AXIS_TOOL_MINOR, gPointerCoordsClassInfo.toolMinor},
+ {AMOTION_EVENT_AXIS_ORIENTATION, gPointerCoordsClassInfo.orientation},
+ {AMOTION_EVENT_AXIS_RELATIVE_X, gPointerCoordsClassInfo.relativeX},
+ {AMOTION_EVENT_AXIS_RELATIVE_Y, gPointerCoordsClassInfo.relativeY},
+ }};
+ for (const auto& [axis, fieldId] : kAxesFromMotionEvent) {
+ const float value = historyPos == HISTORY_CURRENT
+ ? event->getAxisValue(axis, pointerIndex)
+ : event->getHistoricalAxisValue(axis, pointerIndex, historyPos);
+ env->SetFloatField(outPointerCoordsObj, fieldId, value);
+ }
+
+ const PointerCoords* rawPointerCoords = historyPos == HISTORY_CURRENT
+ ? event->getRawPointerCoords(pointerIndex)
+ : event->getHistoricalRawPointerCoords(pointerIndex, historyPos);
+
+ BitSet64 bits = BitSet64(rawPointerCoords->bits);
+ for (const auto [axis, _] : kAxesFromMotionEvent) {
+ bits.clearBit(axis);
+ }
+ pointerCoordsFromNative(env, rawPointerCoords, bits, outPointerCoordsObj);
}
static void android_view_MotionEvent_nativeGetPointerProperties(JNIEnv* env, jclass clazz,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c2c8239..c1de2f4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1488,6 +1488,28 @@
android:description="@string/permdesc_useBiometric"
android:protectionLevel="normal" />
+ <!-- ======================================================================= -->
+ <!-- Permissions for posting notifications -->
+ <!-- ====================================================================== -->
+ <eat-comment />
+
+ <!-- Used for permissions that are associated with posting notifications
+ -->
+ <permission-group android:name="android.permission-group.NOTIFICATIONS"
+ android:icon="@drawable/ic_notifications_alerted"
+ android:label="@string/permgrouplab_notifications"
+ android:description="@string/permgroupdesc_notifications"
+ android:priority="850" />
+
+ <!-- Allows an app to post notifications
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.POST_NOTIFICATIONS"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_postNotification"
+ android:description="@string/permdesc_postNotification"
+ android:protectionLevel="dangerous" />
+
<!-- ====================================================================== -->
<!-- REMOVED PERMISSIONS -->
<!-- ====================================================================== -->
diff --git a/core/res/res/anim-ldrtl/task_close_exit.xml b/core/res/res/anim-ldrtl/task_close_exit.xml
index 76fbdff..0887019 100644
--- a/core/res/res/anim-ldrtl/task_close_exit.xml
+++ b/core/res/res/anim-ldrtl/task_close_exit.xml
@@ -28,9 +28,4 @@
android:startOffset="0"
android:duration="500"/>
- <!-- This is needed to keep the animation running while task_open_enter completes -->
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="1.0"
- android:duration="600"/>
-</set>
\ No newline at end of file
+</set>
diff --git a/core/res/res/anim-ldrtl/task_open_exit.xml b/core/res/res/anim-ldrtl/task_open_exit.xml
index beb6fca..88cdcce 100644
--- a/core/res/res/anim-ldrtl/task_open_exit.xml
+++ b/core/res/res/anim-ldrtl/task_open_exit.xml
@@ -28,9 +28,4 @@
android:startOffset="0"
android:duration="500"/>
- <!-- This is needed to keep the animation running while task_open_enter completes -->
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="1.0"
- android:duration="600"/>
-</set>
\ No newline at end of file
+</set>
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index 736f3f2..3a8dd93 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -30,9 +30,4 @@
android:startOffset="0"
android:duration="500"/>
- <!-- This is needed to keep the animation running while task_open_enter completes -->
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="1.0"
- android:duration="600"/>
-</set>
\ No newline at end of file
+</set>
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index d170317..21fec7f 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -30,9 +30,4 @@
android:startOffset="0"
android:duration="500"/>
- <!-- This is needed to keep the animation running while task_open_enter completes -->
- <alpha
- android:fromAlpha="1.0"
- android:toAlpha="1.0"
- android:duration="600"/>
-</set>
\ No newline at end of file
+</set>
diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml
index 16df452..6c40f10 100644
--- a/core/res/res/drawable/ic_corp_badge.xml
+++ b/core/res/res/drawable/ic_corp_badge.xml
@@ -22,8 +22,5 @@
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
- android:pathData="M20,6h-4V4c0-1.1-0.9-2-2-2h-4C8.9,2,8,2.9,8,4v2H4C2.9,6,2,6.9,2,8l0,11c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8 C22,6.9,21.1,6,20,6z M10,4h4v2h-4V4z M20,19H4V8h16V19z" />
- <path
- android:fillColor="@android:color/white"
- android:pathData="M 12 12 C 12.8284271247 12 13.5 12.6715728753 13.5 13.5 C 13.5 14.3284271247 12.8284271247 15 12 15 C 11.1715728753 15 10.5 14.3284271247 10.5 13.5 C 10.5 12.6715728753 11.1715728753 12 12 12 Z" />
+ android:pathData="@string/config_work_badge_path_24"/>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_corp_badge_case.xml b/core/res/res/drawable/ic_corp_badge_case.xml
index 1cd995e..838426e 100644
--- a/core/res/res/drawable/ic_corp_badge_case.xml
+++ b/core/res/res/drawable/ic_corp_badge_case.xml
@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
- android:viewportWidth="20.0"
- android:viewportHeight="20.0">
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
<path
- android:pathData="M14.67,6.5h-2.33V5.33c0,-0.65 -0.52,-1.17 -1.17,-1.17H8.83c-0.65,0 -1.17,0.52 -1.17,1.17V6.5H5.33c-0.65,0 -1.16,0.52 -1.16,1.17l-0.01,6.42c0,0.65 0.52,1.17 1.17,1.17h9.33c0.65,0 1.17,-0.52 1.17,-1.17V7.67C15.83,7.02 15.31,6.5 14.67,6.5zM10,11.75c-0.64,0 -1.17,-0.52 -1.17,-1.17c0,-0.64 0.52,-1.17 1.17,-1.17c0.64,0 1.17,0.52 1.17,1.17C11.17,11.22 10.64,11.75 10,11.75zM11.17,6.5H8.83V5.33h2.33V6.5z"
+ android:pathData="@string/config_work_badge_path_24"
android:fillColor="#1A73E8"/>
</vector>
diff --git a/core/res/res/drawable/ic_corp_badge_no_background.xml b/core/res/res/drawable/ic_corp_badge_no_background.xml
index 8f7fb70..e81e69f 100644
--- a/core/res/res/drawable/ic_corp_badge_no_background.xml
+++ b/core/res/res/drawable/ic_corp_badge_no_background.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:pathData="M20,6h-4V4c0,-1.11 -0.89,-2 -2,-2h-4C8.89,2 8,2.89 8,4v2H4C2.89,6 2.01,6.89 2.01,8L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8C22,6.89 21.11,6 20,6zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2s2,0.9 2,2S13.1,15 12,15zM14,6h-4V4h4V6z"
+ android:pathData="@string/config_work_badge_path_24"
android:fillColor="#FFFFFF"/>
</vector>
diff --git a/core/res/res/drawable/ic_corp_icon.xml b/core/res/res/drawable/ic_corp_icon.xml
index 48531dd..86bb98e 100644
--- a/core/res/res/drawable/ic_corp_icon.xml
+++ b/core/res/res/drawable/ic_corp_icon.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM14,6h-4L10,4h4v2z"
+ android:pathData="@string/config_work_badge_path_24"
android:fillColor="#000000"/>
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_corp_icon_badge_case.xml b/core/res/res/drawable/ic_corp_icon_badge_case.xml
index 50551d40..09cf9cb 100644
--- a/core/res/res/drawable/ic_corp_icon_badge_case.xml
+++ b/core/res/res/drawable/ic_corp_icon_badge_case.xml
@@ -22,9 +22,15 @@
<path
android:pathData="M 42 42 L 58 42 L 58 58 L 42 58 L 42 42 Z" />
- <path
- android:fillColor="#1A73E8"
- android:pathData="M55.33,46H52.67V44.67a1.33,1.33,0,0,0-1.33-1.33H48.67a1.33,1.33,0,0,0-1.33,1.33V46H44.67a1.32,1.32,0,0,0-1.33,1.33v7.33A1.33,1.33,0,0,0,44.67,56H55.33a1.33,1.33,0,0,0,1.33-1.33V47.33A1.33,1.33,0,0,0,55.33,46ZM50,52a1.33,1.33,0,1,1,1.33-1.33A1.34,1.34,0,0,1,50,52Zm1.33-6H48.67V44.67h2.67Z" />
+ <group
+ android:scaleX=".66"
+ android:scaleY=".66"
+ android:translateX="42"
+ android:translateY="42">
+ <path
+ android:pathData="@string/config_work_badge_path_24"
+ android:fillColor="#1A73E8"/>
+ </group>
<path
android:pathData="M 0 0 H 64 V 64 H 0 V 0 Z" />
</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_corp_statusbar_icon.xml b/core/res/res/drawable/ic_corp_statusbar_icon.xml
index 8f7fb70..e81e69f 100644
--- a/core/res/res/drawable/ic_corp_statusbar_icon.xml
+++ b/core/res/res/drawable/ic_corp_statusbar_icon.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:pathData="M20,6h-4V4c0,-1.11 -0.89,-2 -2,-2h-4C8.89,2 8,2.89 8,4v2H4C2.89,6 2.01,6.89 2.01,8L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8C22,6.89 21.11,6 20,6zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2s2,0.9 2,2S13.1,15 12,15zM14,6h-4V4h4V6z"
+ android:pathData="@string/config_work_badge_path_24"
android:fillColor="#FFFFFF"/>
</vector>
diff --git a/core/res/res/drawable/ic_corp_user_badge.xml b/core/res/res/drawable/ic_corp_user_badge.xml
index a08f2d4..76ba450 100644
--- a/core/res/res/drawable/ic_corp_user_badge.xml
+++ b/core/res/res/drawable/ic_corp_user_badge.xml
@@ -8,9 +8,6 @@
android:pathData="M16.3,11.3h3.4v1.7h-3.4z"
android:fillColor="#FFFFFF"/>
<path
- android:pathData="M18,17.17c-0.92,0 -1.67,0.75 -1.67,1.67c0,0.92 0.75,1.67 1.67,1.67c0.92,0 1.67,-0.75 1.67,-1.67C19.67,17.92 18.92,17.17 18,17.17z"
- android:fillColor="#FFFFFF"/>
- <path
android:pathData="M18,0C8.06,0 0,8.06 0,18s8.06,18 18,18s18,-8.06 18,-18S27.94,0 18,0zM26.3,23.83c0,0.92 -0.71,1.67 -1.63,1.67H11.33c-0.93,0 -1.67,-0.74 -1.67,-1.67l0.01,-9.17c0,-0.92 0.73,-1.67 1.66,-1.67h3.37v-1.67c0,-0.93 0.71,-1.63 1.63,-1.63h3.33c0.93,0 1.63,0.71 1.63,1.63V13h3.37c0.93,0 1.63,0.74 1.63,1.67V23.83z"
android:fillColor="#FFFFFF"/>
</vector>
diff --git a/core/res/res/values-af-television/strings.xml b/core/res/res/values-af-television/strings.xml
new file mode 100644
index 0000000..fcf399b
--- /dev/null
+++ b/core/res/res/values-af-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofoon is geblokkeer"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera is geblokkeer"</string>
+</resources>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index bf8180b..76f4915 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"foonoproepe te maak en te bestuur"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Liggaamsensors"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"toegang te verkry tot sensordata oor jou lewenstekens"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Kennisgewings"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"wys kennisgewings"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Venster-inhoud ophaal"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Die inhoud ondersoek van \'n venster waarmee jy interaksie het."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Verken deur raak aanskakel"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Laat die program toe om die sleutelslot en enige verwante wagwoordsekuriteit te deaktiveer. Byvoorbeeld, die foon deaktiveer die sleutelslot wanneer ’n oproep inkom, en atkiveer dit dan weer wanneer die oproep eindig."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"versoek skermslot-kompleksiteit"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Laat die program toe om die skermslot-kompleksiteitvlak (hoog, medium, laag of geen) te leer, wat die moontlike omvang van die lengte en soort skermslot aandui. Hierdie program kan ook aan gebruikers voorstel dat hulle die skermslot na \'n sekere vlak opdateer, maar gebruikers kan dit uit vrye wil ignoreer en weggaan. Let daarop dat die skermslot nie in skoonteks geberg word nie en die program dus nie die presiese wagwoord ken nie."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"wys kennisgewings"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Laat die program toe om kennisgewings te wys"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"gebruik biometriese hardeware"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Laat die program toe om biometriese hardeware vir stawing te gebruik"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"bestuur vingerafdrukhardeware"</string>
diff --git a/core/res/res/values-am-television/strings.xml b/core/res/res/values-am-television/strings.xml
new file mode 100644
index 0000000..954bfc8
--- /dev/null
+++ b/core/res/res/values-am-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"ማይክሮፎን ታግዷል"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"ካሜራ ታግዷል"</string>
+</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 8f84dd2..5636137 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -326,6 +326,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"የስልክ ጥሪዎች ያድርጉ እና ያስተዳድሩ"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"የሰውነት ዳሳሾች"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"ስለአስፈላጊ ምልክቶችዎ ያሉ የዳሳሽ ውሂብ ይድረሱ"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"የመስኮት ይዘት ሰርስረው ያውጡ"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"መስተጋበር የሚፈጥሩት የመስኮት ይዘት ይመርምሩ።"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"በመንካት ያስሱን ያብሩ"</string>
@@ -549,6 +553,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"መተግበሪያው መቆለፊያውና ማንኛውም የተጎዳኘ የይለፍ ቃል ደህንነት እንዲያሰናክል ይፈቅድለታል። ለምሳሌ ስልኩ ገቢ የስልክ ጥሪ በሚቀበልበት ጊዜ መቆለፊያውን ያሰናክልና ከዚያም ጥሪው ሲጠናቀቅ መቆለፊያውን በድጋሚ ያነቃዋል።"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"የማያ ገጽ መቆለፊያ ውስብስብነትን ጠይቅ"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"መተግበሪያው የማያ ገጽ መቆለፊያው ውስብስብነት ደረጃ (ከፍተኛ፣ መካከለኛ፣ ዝቅተኛ ወይም ምንም) እንዲያውቅ ያስችለዋል፣ ይህም ሊሆኑ የሚችለው የማያ ገጽ መቆለፊያው ርዝመት እና ዓይነት ክልል ያመለክታል። መተግበሪያው እንዲሁም ለተጠቃሚዎች የማያ ገጽ መቆለፊያውን ወደተወሰነ ደረጃ እንዲያዘምኑት ሊጠቁማቸው ይችላል። የማያ ገጽ መቆለፊያው በስነጣ አልባ ጽሑፍ እንደማይከማች ልብ ይበሉ፣ በዚህም መተግበሪያው ትክክለኛውን የይለፍ ቃል አያውቅም።"</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"ባዮሜትራዊ ሃርድዌርን መጠቀም"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"መተግበሪያው የባዮሜትራዊ ሃርድዌር ለማረጋገጥ ስራ እንዲጠቀም ያስችለዋል"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"የጣት አሻራ ሃርድዌርን አስተዳድር"</string>
diff --git a/core/res/res/values-ar-television/strings.xml b/core/res/res/values-ar-television/strings.xml
new file mode 100644
index 0000000..2798656
--- /dev/null
+++ b/core/res/res/values-ar-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"تم حظر الميكروفون"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"تم حظر الكاميرا"</string>
+</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 54821d8..1aaa75e 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -338,6 +338,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"إجراء مكالمات هاتفية وإدارتها"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"أجهزة استشعار الجسم"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"الوصول إلى بيانات المستشعر حول علاماتك الحيوية"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"استرداد محتوى النافذة"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"فحص محتوى نافذة يتم التفاعل معها"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"تفعيل الاستكشاف باللمس"</string>
@@ -561,6 +565,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"للسماح للتطبيق بإيقاف تأمين المفاتيح وأي أمان لكلمة مرور مرتبطة. على سبيل المثال، يعطل الهاتف تأمين المفاتيح عند استقبال مكالمة هاتفية واردة، ثم يعيد تفعيل تأمين المفاتيح عند انتهاء المكالمة."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"طلب معرفة مستوى صعوبة قفل الشاشة"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"للسماح للتطبيق بمعرفة مستوى صعوبة قفل الشاشة (عالي أو متوسط أو منخفض الصعوبة أو بدون)، والذي يحدّد النطاق المحتمل لطول ونوع قفل الشاشة. ويمكن أن يقترح التطبيق للمستخدمين أيضًا تعديل قفل الشاشة إلى مستوى معيّن، ولهم مطلق الحرية في تجاهل هذا الاقتراح ورفضه. وتجدر الإشارة إلى أنه لا يتم حفظ قفل الشاشة في نص عادي، لذا لا يعرف التطبيق كلمة المرور تحديدًا."</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"استخدام الأجهزة البيومترية"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"للسماح للتطبيق باستخدام الأجهزة البيومترية للمصادقة"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"لإدارة أجهزة بصمة الإصبع"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 95aed93..7992f13 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ফ\'ন কল কৰিব আৰু পৰিচলনা কৰিব পাৰে"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"শৰীৰৰ ছেন্সৰ"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"আপোনাৰ দেহৰ গুৰুত্বপূৰ্ণ অংগসমূহৰ অৱস্থাৰ বিষয়ে ছেন্সৰৰ ডেটা লাভ কৰিব পাৰে"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"জাননী"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"জাননী দেখুৱাওক"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ৱিণ্ড’ সমল বিচাৰি উলিওৱাৰ"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"আপুনি চাই থকা ৱিণ্ড’খনৰ সমল পৰীক্ষা কৰাৰ।"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"স্পৰ্শৰ দ্বাৰা অন্বেষণ কৰাৰ সুবিধা অন কৰাৰ"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"এপটোক কী ল\'ক আৰু জড়িত হোৱা যিকোনো পাছৱৰ্ডৰ সুৰক্ষা অক্ষম কৰিব দিয়ে৷ উদাহৰণস্বৰূপে, কোনো অন্তৰ্গামী ফ\'ন কল উঠোৱাৰ সময়ত ফ\'নটোৱে কী-লকটো অক্ষম কৰে, তাৰ পিছত কল শেষ হ\'লেই কী লকটো পুনৰ সক্ষম কৰে৷"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"স্ক্ৰীন লকৰ জটিলতাৰ অনুৰোধ"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"এপ্টোক স্ক্ৰীন লকৰ জটিলতাৰ স্তৰ (উচ্চ, মধ্যম, নিম্ন বা একেবাৰে নাই)ৰ বিষয়ে জানিবলৈ অনুমতি দিয়ে, যিয়ে স্ক্ৰীন লকৰ সম্ভাব্য দৈৰ্ঘ্য বা স্ক্ৰীন লকৰ প্ৰকাৰ দৰ্শায়। লগতে এপ্টোৱে ব্যৱহাৰকাৰীক স্ক্ৰীন লকটো এটা নিৰ্দিষ্ট স্তৰলৈ আপডে’ট কৰিবলৈ পৰামৰ্শ দিব পাৰে যিটো ব্যৱহাৰকাৰীয়ে অৱজ্ঞা কৰি পৰৱর্তী পৃষ্ঠালৈ যাব পাৰে। মনত ৰাখিব যে স্ক্ৰীন লকটো সাধাৰণ পাঠ হিচাপে ষ্ট\'ৰ কৰা নহয়; সেয়েহে, এপ্টোৱে সঠিক পাছৱৰ্ডটো জানিব নোৱাৰে।"</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"জাননী দেখুৱাওক"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"এপ্টোক জাননী দেখুৱাবলৈ দিয়ে"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"বায়োমেট্ৰিক হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰক"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"বিশ্বাসযোগ্য়তা প্ৰমাণীকৰণৰ বাবে এপক বায়োমেট্ৰিক হাৰ্ডৱেৰ ব্য়ৱহাৰ কৰিবলৈ অনুমতি দিয়ে"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ পৰিচালনা কৰিব পাৰে"</string>
diff --git a/core/res/res/values-az-television/strings.xml b/core/res/res/values-az-television/strings.xml
new file mode 100644
index 0000000..c3e0e21
--- /dev/null
+++ b/core/res/res/values-az-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofon blok edilib"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera blok edilib"</string>
+</resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 646175c..d269032 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"telefon zəngləri edin və onları idarə edin"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Bədən sensorları"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"Həyati əlamətlər haqqında sensor dataya daxil olun"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Bildirişlər"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"bildirişləri göstərmək"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Pəncərənin məzmununu əldə edin"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Əlaqədə olduğunuz pəncərənin məzmununu nəzərdən keçirin."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Toxunuşla öyrənmə funksiyasını aktiv edin"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Tətbiqə kilid açarını və təhlükəsizlik parolunu deaktiv etməyə imkan verir. Qanuni misal budur ki, telefon zəng qəbul edən zaman kilidi açır və zəng qurtarandan sonra kilidi bağlayır."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"ekran kilidi mürəkkəbliyi tələb edin"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Tətbiqin ekran kilidinin uzunluğunu və növünü göstərən mürəkəblilik dərəcəsini (yüksək, orta, aşağı və ya heç biri) öyrənməsinə icazə verir. Tətbiq ekran kilidinin müəyyən səviyyəyə yenilənməsini təklif edə bilər. İstifadəçilər bu təklifdən imtina edə və davam edə bilər. Yadda saxlayın ekran kilidi açıq mətn formatında saxlanılmır, buna görə də tətbiq dəqiq parolu bilməyəcək."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"bildirişləri göstərmək"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Tətbiqə bildirişləri göstərmək icazəsi verir"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"biometrik proqramdan istifadə edin"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Doğrulama üçün biometrik proqramdan istifadə etməyə imkan verir"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"barmaq izi avadanlığını idarə edin"</string>
diff --git a/core/res/res/values-b+sr+Latn-television/strings.xml b/core/res/res/values-b+sr+Latn-television/strings.xml
new file mode 100644
index 0000000..df5d8ea
--- /dev/null
+++ b/core/res/res/values-b+sr+Latn-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofon je blokiran"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera je blokirana"</string>
+</resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 3944c1a..1b10856 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -329,6 +329,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"upućuje telefonske pozive i upravlja njima"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Senzori za telo"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"pristupa podacima senzora o vitalnim funkcijama"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Obaveštenja"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"prikazivanje obaveštenja"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"da preuzima sadržaj prozora"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Proverava sadržaj prozora sa kojim ostvarujete interakciju."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"da uključi Istraživanja dodirom"</string>
@@ -552,6 +554,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Dozvoljava aplikaciji da onemogući zaključavanje tastature i sve povezane bezbednosne mere sa lozinkama. Na primer, telefon onemogućava zaključavanje tastature pri prijemu dolaznog telefonskog poziva, a zatim ga ponovo omogućava po završetku poziva."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"traženje složenosti zaključavanja ekrana"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Dozvoljava aplikaciji da sazna nivo složenosti zaključavanja ekrana (visoka, srednja, niska ili nijedna), što ukazuje na mogući opseg trajanja i tip zaključavanja ekrana. Aplikacija može i da predlaže korisnicima da ažuriraju zaključavanje ekrana na određeni nivo, ali korisnici slobodno mogu da zanemare to i da idu na druge stranice. Imajte na umu da se podaci za zaključavanje ekrana ne čuvaju kao običan tekst, pa aplikacija ne zna tačnu lozinku."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"prikazivanje obaveštenja"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Dozvoljava aplikaciji da prikazuje obaveštenja"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"koristi biometrijski hardver"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Dozvoljava aplikaciji da koristi biometrijski hardver za potvrdu identiteta"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"upravljaj hardverom za otiske prstiju"</string>
diff --git a/core/res/res/values-be-television/strings.xml b/core/res/res/values-be-television/strings.xml
new file mode 100644
index 0000000..88d2af1
--- /dev/null
+++ b/core/res/res/values-be-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Мікрафон заблакіраваны"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Камера заблакіравана"</string>
+</resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index a61ea16..d4cd127 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -332,6 +332,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"рабіць тэлефонныя выклікі і кіраваць імі"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Датчыкі цела"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"атрымліваць з датчыка даныя асноўных фізіялагічных паказчыкаў"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Апавяшчэнні"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"паказваць апавяшчэнні"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Атрымліваць змесціва вакна"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Аналізаваць змесціва актыўнага вакна."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Уключаць Азнаямленне дотыкам"</string>
@@ -555,6 +557,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Дазваляе прыкладанням адключаць блакiроўку клавіятуры і любыя сродкі абароны, звязаныя з паролем. Прыкладам гэтага з\'яўляецца адключэнне тэлефонам блакiроўкi клавіятуры пры атрыманні ўваходнага выкліку і паўторнае ўключэнне блакiроўкi клавіятуры, калі выклік завершаны."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"запытваць узровень складанасці блакіроўкі экрана"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Дазваляе праграме вызначаць узровень складанасці блакіроўкі экрана (высокі, сярэдні, нізкі ці нулявы), які залежыць ад даўжыні пароля і ад тыпу блакіроўкі экрана. Праграма можа прапанаваць карыстальнікам ускладніць блакіроўку экрана, аднак гэту прапанову можна ігнараваць. Заўважце, што праграма не можа ведаць тып і пароль блакіроўкі экрана, таму што яны захоўваюцца ў зашыфраваным выглядзе."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"паказваць апавяшчэнні"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Дазваляе праграме паказваць апавяшчэнні"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"выкарыстоўваць біяметрычнае абсталяванне"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Дазваляе праграме выкарыстоўваць для аўтэнтыфікацыі біяметрычнае абсталяванне"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"кіраваць апаратнымі сродкамі для адбіткаў пальцаў"</string>
diff --git a/core/res/res/values-bg-television/strings.xml b/core/res/res/values-bg-television/strings.xml
new file mode 100644
index 0000000..ee96e74
--- /dev/null
+++ b/core/res/res/values-bg-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Микрофонът е блокиран"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Камерата е блокирана"</string>
+</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 43a2a7d..2b057505 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"да извършва телефонни обаждания и да ги управлява"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Сензори за тяло"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"достъп до сензорните данни за жизнените ви показатели"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Известия"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"показване на известията"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Извличане на съдържанието от прозореца"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Инспектиране на съдържанието на прозорец, с който взаимодействате."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Включване на изследването чрез докосване"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Разрешава на приложението да деактивира заключването на клавиатурата и свързаната защита с парола. Например телефонът деактивира заключването при получаване на входящо обаждане и после го активира отново, когато обаждането завърши."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"заявяване на сложност на опцията за заключване на екрана"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Разрешава на приложението да разбере нивото на сложност на опцията за заключване на екрана (високо, средно, ниско или липса на такова), което указва възможния диапазон на дължината и типа на опцията. Приложението може също да предложи на потребителите да актуализират опцията за заключване на екрана до определено ниво, но те могат да пренебрегнат това и да излязат от него. Обърнете внимание, че опцията за заключване на екрана не се съхранява като обикновен текст, така че приложението не знае точната парола."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"показване на известията"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Разрешава на приложението да показва известия"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"използване на хардуера за биометрични данни"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Разрешава на приложението да използва хардуера за биометрични данни с цел удостоверяване"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"управление на хардуера за отпечатъци"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 5a7c0f9..04c8bd5 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -326,6 +326,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ফোন কলগুলি এবং পরিচালনা"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"বডি সেন্সর"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"আপনার অত্যাবশ্যক লক্ষণগুলির সম্পর্কে সেন্সর ডেটা অ্যাক্সেস করে"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"উইন্ডোর কন্টেন্ট পুনরুদ্ধার করে"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ব্যবহার করছেন এমন একটি উইন্ডোর কন্টেন্ট নিরীক্ষণ করে৷"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"স্পর্শের মাধ্যমে অন্বেষণ করা চালু করুন"</string>
@@ -549,6 +553,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"কী-লক এবং যেকোনো সংশ্লিষ্ট পাসওয়ার্ড সুরক্ষা অক্ষম করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ উদাহরণস্বরূপ, একটি ইনকামিং ফোন কল গ্রহণ করার সময়ে ফোনটি কী-লক অক্ষম করে, তারপরে কল শেষ হয়ে গেলে কী-লকটিকে আবার সক্ষম করে৷"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"স্ক্রিন লকের জটিলতা জানার অনুরোধ করুন"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"এটি অ্যাপটিকে স্ক্রিন লকের জটিলতার লেভেল বুঝতে সাহায্য করে (খুব বেশি, মাঝারি, অল্প জটিল বা কোনও জটিলতা নেই), যা স্ক্রিন লকটি সম্ভবত কত দীর্ঘ ও সেটির ধরন কীরকম, তার ইঙ্গিত দেয়। এই অ্যাপটি একটি নির্দিষ্ট লেভেল পর্যন্ত স্ক্রিন লক আপডেট করার সাজেশনও দিতে পারে, তবে ব্যবহারকারী তা উপেক্ষা করে অন্য কোথাও চলে যেতে পারেন। মনে রাখবেন যে স্ক্রিন লক প্লেন টেক্সট হিসেবে সংরক্ষণ করা হয় না, তাই অ্যাপ কখনও আসল পাসওয়ার্ড জানতে পারে না।"</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"বায়োমেট্রিক হার্ডওয়্যার ব্যবহার করুন"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"অ্যাপটিকে যাচাইকরণের জন্য বায়োমেট্রিক হার্ডওয়্যার ব্যবহার করার অনুমতি দেয়"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার পরিচালনা করুন"</string>
diff --git a/core/res/res/values-bs-television/strings.xml b/core/res/res/values-bs-television/strings.xml
new file mode 100644
index 0000000..df5d8ea
--- /dev/null
+++ b/core/res/res/values-bs-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofon je blokiran"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera je blokirana"</string>
+</resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index f9157ae..32bc006 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -329,6 +329,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"poziva i upravlja pozivima"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Tjelesni senzori"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"pristupa podacima senzora o vašim vitalnim funkcijama"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Obavještenja"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"prikaz obavještenja"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Preuzima sadržaj prozora"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Pregleda sadržaj prozora koji trenutno koristite."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Uključi opciju Istraživanje dodirom"</string>
@@ -552,6 +554,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Omogućava aplikaciji deaktivaciju zaključane tastature i svih povezanih zaštita. Naprimjer, telefon deaktivira zaključavanje tastature kod dolaznog telefonskog poziva, a zatim ponovo aktivira zaključavanje tastature kada je poziv završen."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"zahtjev za kompleksnost zaključavanja ekrana"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Omogućava aplikaciji da sazna nivo kompleksnosti zaključavanja ekrana (visoki, srednji, niski ili bez zaključavanja), što naznačava mogući raspon trajanja i vrste zaključavanja ekrana. Aplikacija također može korisnicima predložiti da ažuriraju zaključavanje ekrana do određenog nivoa, ali korisnici slobodno mogu ignorirati prijedlog i napustiti stranicu. Važno je napomenuti da se zaključavanje ekrana ne pohranjuje kao obični tekst tako da aplikacija ne zna tačnu lozinku."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"prikaz obavještenja"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Dozvoljava aplikaciji da prikazuje obavještenja"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"koristi biometrijski hardver za otiske prstij"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Omogućava aplikaciji da za autentifikaciju koristi biometrijski hardver"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"upravljanje hardverom za otisak prsta"</string>
diff --git a/core/res/res/values-ca-television/strings.xml b/core/res/res/values-ca-television/strings.xml
new file mode 100644
index 0000000..5f370f4
--- /dev/null
+++ b/core/res/res/values-ca-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"El micròfon està bloquejat"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"La càmera està bloquejada"</string>
+</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index df98f93..4bc1ea7 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"fer i gestionar trucades telefòniques"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensors corporals"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"accedir a les dades del sensor sobre les constants vitals"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificacions"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"mostra notificacions"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperar el contingut de la finestra"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecciona el contingut d\'una finestra amb què estàs interaccionant."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activar Exploració tàctil"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Permet que l\'aplicació desactivi el bloqueig del teclat i qualsevol element de seguretat de contrasenyes associat. Per exemple, el telèfon desactiva el bloqueig del teclat en rebre una trucada entrant i, a continuació, reactiva el bloqueig del teclat quan finalitza la trucada."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"sol·licita una determinada complexitat del bloqueig de pantalla"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permet que l\'aplicació conegui el nivell de complexitat del bloqueig de pantalla (alt, mitjà, baix o cap), que indica la llargària i el tipus de bloqueig de pantalla possibles. L\'aplicació també pot suggerir que els usuaris actualitzin el bloqueig de pantalla a un nivell determinat, però els usuaris poden ignorar aquestes recomanacions. Tingues en compte que el bloqueig de pantalla no s\'emmagatzema com a text sense format, de manera que l\'aplicació no coneix la contrasenya exacta."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"mostra notificacions"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Permet que l\'aplicació mostri notificacions"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"utilitza maquinari biomètric"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Permet que l\'aplicació faci servir maquinari biomètric per a l\'autenticació"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"Gestionar el maquinari d\'empremtes digitals"</string>
diff --git a/core/res/res/values-cs-television/strings.xml b/core/res/res/values-cs-television/strings.xml
new file mode 100644
index 0000000..f299610
--- /dev/null
+++ b/core/res/res/values-cs-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofon je zablokován"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera je zablokována"</string>
+</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 7f0f977..4de0251 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -332,6 +332,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"uskutečňování a spravování telefonních hovorů"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Tělesné senzory"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"přístup k datům ze snímačů vašich životních funkcí"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Oznámení"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"zobrazovat oznámení"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Načítat obsah oken"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Může prozkoumávat obsah oken, se kterými pracujete."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Zapnout funkci Prozkoumání dotykem"</string>
@@ -555,6 +557,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Umožňuje aplikaci vypnout zámek kláves a související zabezpečení heslem. Telefon například vypne zámek klávesnice při příchozím hovoru a po skončení hovoru jej zase zapne."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"zjištění složitosti zámku obrazovky"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Umožňuje aplikaci zjistit úroveň složitosti zámku obrazovky (vysoká, střední, nízká nebo žádná), která ukazuje možnou délku a typ zámku obrazovky. Aplikace také může uživatelům navrhovat, aby zámek obrazovky upravili na určitou úroveň, ale uživatelé mohou návrhy klidně ignorovat a odejít. Zámek obrazovky není uložen jako prostý text, a tak aplikace přesné heslo nezná."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"zobrazovat oznámení"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Povolí aplikaci zobrazovat oznámení"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"použití biometrického hardwaru"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Umožňuje aplikaci použít k ověření biometrický hardware"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"správa hardwaru na čtení otisků prstů"</string>
diff --git a/core/res/res/values-da-television/strings.xml b/core/res/res/values-da-television/strings.xml
new file mode 100644
index 0000000..7645392
--- /dev/null
+++ b/core/res/res/values-da-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofonen er blokeret"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kameraet er blokeret"</string>
+</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index c0a1d28..9662952 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"foretage og administrere telefonopkald"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Kropssensorer"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"få adgang til sensordata om dine livstegn"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notifikationer"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"vise notifikationer"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Hente indholdet i vinduet"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Undersøge indholdet i et vindue, du interagerer med."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Aktivere Udforsk ved berøring"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Tillader, at appen kan deaktivere tastaturlåsen og anden form for tilknyttet adgangskodesikkerhed. Telefonen deaktiverer f.eks. tastaturlåsen ved indgående telefonopkald og aktiverer tastaturlåsen igen, når opkaldet er afsluttet."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"anmode om skærmlåsens kompleksitet"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Giver appen tilladelse til at kende skærmlåsens kompleksitet (høj, medium, lav eller ingen), hvilket kan afsløre oplysninger om skærmlåsens længde og type. Appen kan også foreslå brugerne at opdatere deres skærmlås til et bestemt niveau, men brugerne kan frit ignorere det og gå videre. Bemærk! Skærmlåsen gemmes ikke som almindelig tekst, så appen kender ikke den nøjagtige adgangskode."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"vise notifikationer"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Giver appen tilladelse til at vise notifikationer"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"brug biometrisk hardware"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Tillader, at appen kan bruge biometrisk hardware til godkendelse"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"administrer hardware til fingeraftryk"</string>
diff --git a/core/res/res/values-de-television/strings.xml b/core/res/res/values-de-television/strings.xml
new file mode 100644
index 0000000..2509410
--- /dev/null
+++ b/core/res/res/values-de-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Das Mikrofon ist blockiert"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Die Kamera ist blockiert"</string>
+</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index a2b90e4..1bd7970 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -326,6 +326,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"Telefonanrufe tätigen und verwalten"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Körpersensoren"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"auf Sensordaten zu deinen Vitaldaten zugreifen"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Fensterinhalte abrufen"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Die Inhalte eines Fensters, mit dem du interagierst, werden abgerufen."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"\"Tippen & Entdecken\" aktivieren"</string>
@@ -549,6 +553,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Ermöglicht der App, die Tastensperre sowie den damit verbundenen Passwortschutz zu deaktivieren. Das Telefon deaktiviert die Tastensperre beispielsweise, wenn ein Anruf eingeht, und aktiviert sie wieder, nachdem das Gespräch beendet wurde."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"Komplexitätsstufe der Displaysperre anfragen"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Ermöglicht es der App, die Komplexitätsstufe der Displaysperre (hoch, mittel, niedrig oder keine) zu ermitteln, was auf Art und mögliche Dauer der Displaysperre hinweist. Die App kann Nutzern auch vorschlagen, die Displaysperre auf eine bestimmte Stufe zu setzen. Nutzer können diesen Vorschlag jedoch einfach ignorieren und fortfahren. Beachten Sie, dass die Displaysperre nicht im Klartext gespeichert ist, sodass die App nicht das genaue Passwort kennt."</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"Biometrische Hardware verwenden"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Erlaubt der App, biometrische Hardware zur Authentifizierung zu verwenden"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"Fingerabdruckhardware verwalten"</string>
diff --git a/core/res/res/values-el-television/strings.xml b/core/res/res/values-el-television/strings.xml
new file mode 100644
index 0000000..5024288
--- /dev/null
+++ b/core/res/res/values-el-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Το μικρόφωνο είναι αποκλεισμένο"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Η κάμερα είναι αποκλεισμένη"</string>
+</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index ac16e64..57598f4 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"πραγματοποιεί και να διαχειρίζεται τηλ/κές κλήσεις"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Αισθητήρες σώματος"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"πρόσβαση στα δεδομένα αισθητήρα σχετικά με τις ζωτικές ενδείξεις σας"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Ειδοποιήσεις"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"εμφάνιση ειδοποιήσεων"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Ανάκτηση του περιεχομένου του παραθύρου"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Έλεγχος του περιεχομένου ενός παραθύρου με το οποίο αλληλεπιδράτε."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Ενεργοποίηση της \"Εξερεύνησης με άγγιγμα\""</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Επιτρέπει στην εφαρμογή την απενεργοποίηση του κλειδώματος πληκτρολογίου και άλλης σχετικής ασφάλειας με κωδικό πρόσβασης. Για παράδειγμα, το κλείδωμα πληκτρολογίου στο τηλέφωνο απενεργοποιείται όταν λαμβάνεται εισερχόμενη τηλεφωνική κλήση και ενεργοποιείται ξανά όταν η κλήση τερματιστεί."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"υποβολή αιτήματος για πολυπλοκότητα οθόνης κλειδώματος"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Επιτρέπει στην εφαρμογή να μάθει το επίπεδο πολυπλοκότητας του κλειδώματος οθόνης (υψηλό, μέσο, χαμηλό ή κανένα), το οποίο υποδεικνύει το πιθανό εύρος του μήκους και του τύπου κλειδώματος οθόνης. Η εφαρμογή μπορεί επίσης να προτείνει στους χρήστες να ενημερώσουν το κλείδωμα οθόνης σε ένα συγκεκριμένο επίπεδο, όμως οι χρήστες μπορούν να την αγνοήσουν και να συνεχίσουν. Λάβετε υπόψη ότι το κλείδωμα οθόνης δεν αποθηκεύεται σε απλό κείμενο. Συνεπώς, η εφαρμογή δεν γνωρίζει τον κωδικό."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"εμφάνιση ειδοποιήσεων"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Επιτρέπει στην εφαρμογή να εμφανίζει ειδοποιήσεις"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"χρήση βιομετρικού εξοπλισμού"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί βιομετρικό εξοπλισμό για έλεγχο ταυτότητας"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"διαχείριση εξοπλισμού δακτυλικού αποτυπώματος"</string>
diff --git a/core/res/res/values-en-rAU-television/strings.xml b/core/res/res/values-en-rAU-television/strings.xml
new file mode 100644
index 0000000..c4b586f
--- /dev/null
+++ b/core/res/res/values-en-rAU-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Microphone is blocked"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Camera is blocked"</string>
+</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 0b02d69..62fe27d 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"make and manage phone calls"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Body sensors"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"access sensor data about your vital signs"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notifications"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"show notifications"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Retrieve window content"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspect the content of a window you\'re interacting with."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Turn on Explore by Touch"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"request screen lock complexity"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"show notifications"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Allows the app to show notifications"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"use biometric hardware"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Allows the app to use biometric hardware for authentication"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"manage fingerprint hardware"</string>
diff --git a/core/res/res/values-en-rCA-television/strings.xml b/core/res/res/values-en-rCA-television/strings.xml
new file mode 100644
index 0000000..c4b586f
--- /dev/null
+++ b/core/res/res/values-en-rCA-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Microphone is blocked"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Camera is blocked"</string>
+</resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index c9167614..4257d94 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"make and manage phone calls"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Body sensors"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"access sensor data about your vital signs"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notifications"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"show notifications"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Retrieve window content"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspect the content of a window you\'re interacting with."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Turn on Explore by Touch"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"request screen lock complexity"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"show notifications"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Allows the app to show notifications"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"use biometric hardware"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Allows the app to use biometric hardware for authentication"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"manage fingerprint hardware"</string>
diff --git a/core/res/res/values-en-rGB-television/strings.xml b/core/res/res/values-en-rGB-television/strings.xml
new file mode 100644
index 0000000..c4b586f
--- /dev/null
+++ b/core/res/res/values-en-rGB-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Microphone is blocked"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Camera is blocked"</string>
+</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 34eeff4..a686500 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"make and manage phone calls"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Body sensors"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"access sensor data about your vital signs"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notifications"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"show notifications"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Retrieve window content"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspect the content of a window that you\'re interacting with."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Turn on Explore by Touch"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"request screen lock complexity"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"show notifications"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Allows the app to show notifications"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"use biometric hardware"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Allows the app to use biometric hardware for authentication"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"manage fingerprint hardware"</string>
diff --git a/core/res/res/values-en-rIN-television/strings.xml b/core/res/res/values-en-rIN-television/strings.xml
new file mode 100644
index 0000000..c4b586f
--- /dev/null
+++ b/core/res/res/values-en-rIN-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Microphone is blocked"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Camera is blocked"</string>
+</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index ef7924e..89dc230 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"make and manage phone calls"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Body sensors"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"access sensor data about your vital signs"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notifications"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"show notifications"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Retrieve window content"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspect the content of a window you\'re interacting with."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Turn on Explore by Touch"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"request screen lock complexity"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plain text so the app does not know the exact password."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"show notifications"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Allows the app to show notifications"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"use biometric hardware"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Allows the app to use biometric hardware for authentication"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"manage fingerprint hardware"</string>
diff --git a/core/res/res/values-en-rXC-television/strings.xml b/core/res/res/values-en-rXC-television/strings.xml
new file mode 100644
index 0000000..5b8b9bb
--- /dev/null
+++ b/core/res/res/values-en-rXC-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Microphone is blocked"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Camera is blocked"</string>
+</resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 7aa9f44..2d8d038 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"make and manage phone calls"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Body sensors"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"access sensor data about your vital signs"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notifications"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"show notifications"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Retrieve window content"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspect the content of a window you\'re interacting with."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Turn on Explore by Touch"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Allows the app to disable the keylock and any associated password security. For example, the phone disables the keylock when receiving an incoming phone call, then re-enables the keylock when the call is finished."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"request screen lock complexity"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Allows the app to learn the screen lock complexity level (high, medium, low or none), which indicates the possible range of length and type of the screen lock. The app can also suggest to users that they update the screen lock to a certain level but users can freely ignore and navigate away. Note that the screen lock is not stored in plaintext so the app does not know the exact password."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"show notifications"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Allows the app to show notifications"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"use biometric hardware"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Allows the app to use biometric hardware for authentication"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"manage fingerprint hardware"</string>
diff --git a/core/res/res/values-es-rUS-television/strings.xml b/core/res/res/values-es-rUS-television/strings.xml
new file mode 100644
index 0000000..d486aa0
--- /dev/null
+++ b/core/res/res/values-es-rUS-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"El micrófono está bloqueado"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"La cámara está bloqueada"</string>
+</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index d066657..843d04f 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"hacer y administrar llamadas telefónicas"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensores corporales"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"acceder a los datos del sensor acerca de tus signos vitales"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificaciones"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"mostrar notificaciones"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperar el contenido de las ventanas"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecciona el contenido de la ventana con la que estés interactuando."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activar la Exploración táctil"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Permite que la aplicación desactive el bloqueo del teclado y cualquier protección con contraseña asociada. Por ejemplo, el dispositivo puede desactivar el bloqueo del teclado cuando recibe una llamada telefónica y volver a activarlo cuando finaliza la llamada."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"solicitar complejidad del bloqueo de pantalla"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permite que la app conozca el nivel de complejidad del bloqueo de pantalla (alta, media, baja o ninguna), lo que indica el rango de duración posible y el tipo de bloqueo. La app también puede sugerirles a los usuarios que actualicen el bloqueo de pantalla a un determinado nivel, aunque ellos pueden ignorar esta sugerencia y seguir navegando. Ten en cuenta que el bloqueo de pantalla no se almacena como texto sin formato, por lo que la app no conoce la contraseña exacta."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"mostrar notificaciones"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Permite que la app muestre notificaciones"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"usar hardware biométrico"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Permite que la app use hardware biométrico para realizar la autenticación"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"Administrar el hardware de huellas dactilares"</string>
diff --git a/core/res/res/values-es-television/strings.xml b/core/res/res/values-es-television/strings.xml
new file mode 100644
index 0000000..d486aa0
--- /dev/null
+++ b/core/res/res/values-es-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"El micrófono está bloqueado"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"La cámara está bloqueada"</string>
+</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index c4b1a0d..8127201 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"hacer y administrar llamadas telefónicas"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensores corporales"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"acceder a datos de sensores de tus constantes vitales"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificaciones"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"mostrar notificaciones"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Comprobar el contenido de la ventana"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecciona el contenido de una ventana con la que estés interactuando."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activar la exploración táctil"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Permite que la aplicación inhabilite el bloqueo del teclado y cualquier protección con contraseña asociada. Por ejemplo, el teléfono puede inhabilitar el bloqueo del teclado cuando se recibe una llamada telefónica y volver a habilitarlo cuando finaliza la llamada."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"solicitar complejidad del bloqueo de pantalla"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permite que la aplicación entienda el nivel de complejidad del bloqueo de pantalla (alto, medio, bajo o ninguno) que indica la longitud y el tipo del bloqueo de pantalla posibles. La aplicación también puede sugerir a los usuarios que actualicen el bloqueo de pantalla para que tenga un nivel concreto de complejidad, pero los usuarios pueden ignorar la advertencia libremente. El bloqueo de pantalla no se almacena en texto sin formato, así que la aplicación no puede saber cuál es la contraseña exacta."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"mostrar notificaciones"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Permite que la aplicación muestre notificaciones"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"usar hardware biométrico"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Permite que la aplicación utilice el hardware biométrico para realizar la autenticación"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"administrar hardware de huellas digitales"</string>
diff --git a/core/res/res/values-et-television/strings.xml b/core/res/res/values-et-television/strings.xml
new file mode 100644
index 0000000..56a7918
--- /dev/null
+++ b/core/res/res/values-et-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofon on blokeeritud"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kaamera on blokeeritud"</string>
+</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 63066f5..510826e 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"helistamine ja telefonikõnede haldamine"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Kehaandurid"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"juurdepääs anduri andmetele teie eluliste näitajate kohta"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Märguanded"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"märguannete kuvamine"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Akna sisu toomine"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Kasutatava akna sisu kontrollimine."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Puudutusega sirvimise sisselülitamine"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Võimaldab rakendusel keelata klahviluku ja muu seotud parooli turvalisuse. Näiteks keelab telefon klahviluku sissetuleva kõne vastuvõtmisel ja lubab klahviluku uuesti, kui kõne on lõpetatud."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"ekraaniluku keerukuse taotlemine"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Lubab rakendusel vaadata ekraaniluku keerukuse taset (kõrge, keskmine, madal või puudub), mis näitab ekraaniluku võimalikku pikkust ja tüüpi. Rakendus võib kasutajatele soovitada ka ekraaniluku viimist teatud tasemele, kuid kasutajad võivad seda eirata ja kuvalt lahkuda. Pange tähele, et ekraanilukku ei salvestata lihttekstina, seega ei tea rakendus täpset parooli."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"märguannete kuvamine"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Lubab rakendusel märguandeid kuvada"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"kasutada biomeetrilist riistvara"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Võimaldab rakendusel autentimiseks kasutada biomeetrilist riistvara"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"sõrmejälje riistvara haldamine"</string>
diff --git a/core/res/res/values-eu-television/strings.xml b/core/res/res/values-eu-television/strings.xml
new file mode 100644
index 0000000..c0f3c1e
--- /dev/null
+++ b/core/res/res/values-eu-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Blokeatuta dago mikrofonoa"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Blokeatuta dago kamera"</string>
+</resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index ca187cb..5706aa1 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"egin eta kudeatu telefono-deiak"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Gorputz-sentsoreak"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"atzitu bizi-konstanteei buruzko sentsorearen datuak"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Jakinarazpenak"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"jakinarazpenak erakutsi"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Eskuratu leihoko edukia"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Arakatu irekita daukazun leihoko edukia."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Aktibatu \"Arakatu ukituta\""</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Teklen blokeoa eta erlazionatutako pasahitz-segurtasuna desgaitzeko baimena ematen die aplikazioei. Adibidez, telefonoak teklen blokeoa desgaitzen du telefono-deiak jasotzen dituenean, eta berriro gaitzen du deiak amaitzean."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"eskatu pantailaren blokeoa konplexua izatea"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Pantailaren blokeoaren konplexutasun-maila (handia, ertaina, txikia edo bat ere ez) jakiteko baimena ematen die aplikazioei. Informazio horrekin, pantailaren blokeoaren luzera-barruti edo mota posiblea ondoriozta liteke. Halaber, pantailaren blokeoa maila jakin batera igotzeko iradoki diezaiekete aplikazioek erabiltzaileei, baina horri ez ikusi egin eta aplikazioak erabiltzen jarraitzeko aukera dute erabiltzaileek. Kontuan izan pantailaren blokeoa ez dela gordetzen testu arrunt gisa; beraz, aplikazioek ez dute jakingo pasahitz zehatza zein den."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"jakinarazpenak erakutsi"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Jakinarazpenak erakusteko baimena ematen die aplikazioei"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"erabili hardware biometrikoa"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Autentifikatzeko hardware biometrikoa erabiltzeko baimena ematen die aplikazioei."</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"kudeatu hatz-marken hardwarea"</string>
diff --git a/core/res/res/values-fa-television/strings.xml b/core/res/res/values-fa-television/strings.xml
new file mode 100644
index 0000000..173d056
--- /dev/null
+++ b/core/res/res/values-fa-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"میکروفن مسدود شده است"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"دوربین مسدود شده است"</string>
+</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index a99aaeee..3eb3f3d 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"برقراری و مدیریت تماسهای تلفنی"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"حسگرهای بدن"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"دسترسی به دادههای حسگر در رابطه با علائم حیاتی شما"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"اعلانها"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"نمایش اعلان"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"محتوای پنجره را بازیابی کند"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"محتوای پنجرهای را که درحال تعامل با آن هستید بررسی میکند."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"فعالسازی کاوش لمسی"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"به برنامه امکان میدهد قفل کلید و هر گونه امنیت گذرواژه مرتبط را غیرفعال کند. بهعنوان مثال تلفن هنگام دریافت یک تماس تلفنی ورودی قفل کلید را غیرفعال میکند و بعد از پایان تماس، قفل کلید را دوباره فعال میکند."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"درخواست پیچیدگی قفل صفحه"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"به برنامه اجازه میدهد سطح پیچیدگی قفل صفحه (بالا، متوسط، پایین، یا هیچکدام) را بیاموزد که نشاندهنده بازه ممکن طول و نوع قفل صفحه است. همچنین برنامه میتواند به کاربران پیشنهاد دهد قفل صفحه را به سطح خاصی بهروزرسانی کنند، اما کاربران میتوانند آزادانه این پیشنهاد را نادیده بگیرند و به سطح دیگری بروند. توجه داشته باشید که قفل صفحه در قالب نوشتار ساده ذخیره نمیشود، بنابراین برنامه گذرواژه دقیق را نمیداند."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"نمایش اعلان"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"به این برنامه اجازه میدهد اعلان نمایش دهد"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"استفاده از سختافزار بیومتریک"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"به برنامه امکان میدهد از سختافزار بیومتریک برای اصالتسنجی استفاده کند"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"مدیریت سختافزار اثر انگشت"</string>
diff --git a/core/res/res/values-fi-television/strings.xml b/core/res/res/values-fi-television/strings.xml
new file mode 100644
index 0000000..ab45635
--- /dev/null
+++ b/core/res/res/values-fi-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofoni on estetty"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera on estetty"</string>
+</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index bf7eb9d..d40ed60 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"soittaa ja hallinnoida puheluita"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Kehon anturit"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"käyttää anturitietoja elintoiminnoistasi"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Ilmoitukset"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"näyttää ilmoituksia"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Noutaa ikkunan sisältöä"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Tarkistaa käyttämäsi ikkunan sisältö."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Ottaa kosketuksella tutkimisen käyttöön"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Antaa sovelluksen ottaa näppäinlukon ja siihen liittyvän salasanasuojauksen pois käytöstä. Esimerkki: puhelin poistaa näppäinlukon käytöstä puhelun saapuessa ja asettaa lukon takaisin käyttöön puhelun päättyessä."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"pyytää näytön lukituksen monimutkaisuutta"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Sovellus saa luvan selvittää näytön lukituksen monimutkaisuuden (korkea, keskitaso tai matala) eli sen pituusluokan ja tyypin. Sovellus voi myös ehdottaa käyttäjille näytön lukituksen vaihtamista tietylle tasolle, mutta he voivat ohittaa tämän ja poistua. Itse näytön lukitusta ei säilytetä tekstimuodossa, joten sovellus ei tiedä sitä tarkalleen."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"näyttää ilmoituksia"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Antaa sovelluksen näyttää ilmoituksia"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"käytä biometristä laitteistoa"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Sallii sovelluksen käyttää biometristä laitteistoa todennukseen"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"sormenjälkilaitteiston hallinnointi"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 2ccf9ed..a8023cc 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -326,6 +326,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"faire et gérer des appels téléphoniques"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Capteurs corporels"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"accéder aux données des capteurs sur vos signes vitaux"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Récupérer le contenu d\'une fenêtre"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecter le contenu d\'une fenêtre avec laquelle vous interagissez."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activer la fonctionnalité Explorer au toucher"</string>
@@ -549,6 +553,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Permet à l\'application de désactiver le verrouillage des touches et toute mesure de sécurité par mot de passe associée. Par exemple, votre téléphone désactive le verrouillage des touches lorsque vous recevez un appel, puis le réactive lorsque vous raccrochez."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"demander la complexité du verrouillage d\'écran"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Autorise l\'application à apprendre le niveau de complexité de l\'écran de verrouillage (élevé, moyen, faible ou aucun), qui indique la gamme possible de longueur et de type de verrouillage d\'écran. L\'application peut aussi suggérer aux utilisateurs de mettre à jour l\'écran de verrouillage afin d\'utiliser un certain niveau de complexité, mais ils peuvent ignorer la suggestion. Notez que le verrouillage d\'écran n\'est pas stocké en texte brut pour de manière à ce que l\'application n\'ait pas accès au mot de passe exact."</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"utiliser le matériel biométrique"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Permet à l\'application d\'utiliser du matériel biométrique pour l\'authentification"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"gérer le matériel d\'empreinte digitale"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 0ff73f8..a88df26 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"effectuer et gérer des appels téléphoniques"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Capteurs corporels"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"accéder aux données des capteurs relatives à vos signes vitaux"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notifications"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"afficher des notifications"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Récupérer le contenu d\'une fenêtre"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecte le contenu d\'une fenêtre avec laquelle vous interagissez."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activer la fonctionnalité Explorer au toucher"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Permet à l\'application de désactiver le verrouillage des touches et toute mesure de sécurité via mot de passe associée. Par exemple, votre téléphone désactive le verrouillage des touches lorsque vous recevez un appel, puis le réactive lorsque vous raccrochez."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"demander la complexité du verrouillage de l\'écran"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permet à l\'application de connaître le niveau de complexité du verrouillage de l\'écran (élevé, moyen, faible ou aucun), qui indique les possibilités en matière de longueur du mot de passe et de type de verrouillage de l\'écran. L\'application peut également suggérer aux utilisateurs de modifier la complexité du verrouillage, mais ces derniers peuvent librement l\'ignorer. Remarque : Comme le verrouillage de l\'écran n\'est pas stocké au format texte brut, l\'application ne connaît pas le mot de passe exact."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"afficher des notifications"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Autorise l\'appli à afficher des notifications"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"utiliser les composants biométriques"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Autoriser l\'application à utiliser les composants biométriques pour l\'authentification"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"Gérer le matériel d\'empreintes digitales"</string>
diff --git a/core/res/res/values-gl-television/strings.xml b/core/res/res/values-gl-television/strings.xml
new file mode 100644
index 0000000..f864e13
--- /dev/null
+++ b/core/res/res/values-gl-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"O micrófono está bloqueado"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"A cámara está bloqueada"</string>
+</resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 6da942d..74df014 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"facer e xestionar chamadas telefónicas"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensores corporais"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"acceder aos datos dos sensores sobre as túas constantes vitais"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificacións"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"mostra notificacións"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperar contido da ventá"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecciona o contido dunha ventá coa que estás interactuando."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activar a exploración táctil"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Permite á aplicación desactivar o bloqueo do teclado e calquera seguranza dos contrasinais asociada. Por exemplo, o teléfono desactiva o bloqueo do teclado ao recibir unha chamada telefónica entrante e, a continuación, volve activar o bloqueo do teclado unha vez finalizada a chamada."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"solicitar o nivel de complexidade do bloqueo de pantalla"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permite que a aplicación consulte o nivel (alto, medio, baixo ou inexistente) de complexidade do bloqueo de pantalla para saber a lonxitude aproximada do contrasinal e o tipo de bloqueo de pantalla. A aplicación tamén pode suxerirlles aos usuarios que aumenten o nivel de complexidade do bloqueo de pantalla, aínda que poden ignorar a suxestión e seguir navegando. Ten en conta que o bloqueo de pantalla non se almacena como texto sen formato, polo que a aplicación non pode consultar o contrasinal exacto."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"mostrar notificacións"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Permite que a aplicación mostre notificacións"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"utilizar hardware biométrico"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Permite que a aplicación utilice hardware biométrico para a autenticación"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"xestionar hardware de impresión dixital"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 5eea849..02b1787 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -326,6 +326,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ફોન કૉલ કરો અને મેનેજ કરો"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"બૉડી સેન્સર"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"તમારા મહત્વપૂર્ણ ચિહ્નો વિશે સેન્સર ડેટા ઍક્સેસ કરો"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"વિંડો કન્ટેન્ટ પુનઃપ્રાપ્ત કરો"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"તમે જેની સાથે ક્રિયા-પ્રતિક્રિયા કરી રહ્યાં છો તે વિંડોનું કન્ટેન્ટ તપાસો."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"સ્પર્શ કરીને શોધખોળ કરવું ચાલુ કરો"</string>
@@ -549,6 +553,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"એપ્લિકેશનને કીલૉક અને કોઈપણ સંકળાયેલ પાસવર્ડ સુરક્ષા અક્ષમ કરવાની મંજૂરી આપે છે. ઉદાહરણ તરીકે, ઇનકમિંગ ફોન કૉલ પ્રાપ્ત કરતી વખતે ફોન, કીલૉકને અક્ષમ કરે છે, પછી કૉલ સમાપ્ત થઈ જવા પર કીલૉક ફરીથી સક્ષમ કરે છે."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"સ્ક્રીન લૉકની જટિલતા જાણવા માટે વિનંતી કરો"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"ઍપને સ્ક્રીન લૉકની જટિલતાનું લેવલ (ઊંચું, મધ્યમ, નીચું અથવા કોઈ નહીં) જાણવાની મંજૂરી આપે છે, જે સ્ક્રીન લૉકના પ્રકાર અને લંબાઈની સંભવિત શ્રેણી સૂચવે છે. ઍપ વપરાશકર્તાઓને સ્ક્રીન લૉકને ચોક્કસ લેવલ સુધી અપડેટ કરવાનું સૂચન પણ કરી શકે છે, પરંતુ વપરાશકર્તાઓ મુક્ત રીતે અવગણીને નૅવિગેટ કરી શકે છે. નોંધી લો કે સ્ક્રીન લૉકનો plaintextમાં સંગ્રહ કરવામાં આવતો નથી, તેથી ઍપને ચોક્કસ પાસવર્ડની જાણ હોતી નથી."</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"બાયોમેટ્રિક હાર્ડવેરનો ઉપયોગ કરો"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"ઍપને પ્રમાણીકરણ માટે બાયોમેટ્રિક હાર્ડવેરનો ઉપયોગ કરવાની મંજૂરી આપે છે"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"ફિંગરપ્રિન્ટ હાર્ડવેરને મેનેજ કરો"</string>
diff --git a/core/res/res/values-hi-television/strings.xml b/core/res/res/values-hi-television/strings.xml
new file mode 100644
index 0000000..ad3b265
--- /dev/null
+++ b/core/res/res/values-hi-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"माइक्रोफ़ोन को ब्लॉक किया गया है"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"कैमरे को ब्लॉक किया गया है"</string>
+</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index fb8afe4..e7cdae4 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"फ़ोन कॉल करने और उन्हें प्रबंधित करने की अनुमति दें"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"बॉडी सेंसर"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"अपने महत्वपूर्ण संकेतों के बारे में सेंसर डेटा को ऐक्सेस करें"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"सूचनाएं"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"सूचनाएं दिखाएं"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"विंडो का कॉन्टेंट वापस पाएं"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"उस विंडो की सामग्री की जाँच करें, जिसका आप इस्तेमाल कर रहे हैं."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"छूकर, किसी चीज़ से जुड़ी जानकारी सुनने की सुविधा चालू करें"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"ऐप्स को कीलॉक और कोई भी संबद्ध पासवर्ड सुरक्षा बंद करने देता है. उदाहरण के लिए, इनकमिंग फ़ोन कॉल पाते समय फ़ोन, कीलॉक को बंद कर देता है, फिर कॉल खत्म होने पर कीलॉक को फिर से चालू कर देता है."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"जानें कि स्क्रीन लॉक कितना मुश्किल बनाया गया है"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"यह मंज़ूरी मिलने के बाद ऐप्लिकेशन जान पाता है कि स्क्रीन लॉक कितना मुश्किल (बहुत ज़्यादा, मध्यम, कम या बिल्कुल नहीं) है. इस स्तर से यह पता चलता है कि स्क्रीन लॉक कितना लंबा या किस तरह का है. ऐप्लिकेशन उपयोगकर्ताओं को यह सुझाव भी दे सकता है कि वे स्क्रीन लॉक को एक तय लेवल तक अपडेट करें. लेकिन उपयोगकर्ता इसे बेझिझक अनदेखा करके छोड़ सकते हैं. ध्यान दें कि स्क्रीन लॉक को सादे टेक्स्ट में सेव नहीं किया जाता है इसलिए ऐप्लिकेशन को सटीक पासवर्ड पता नहीं होता है."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"सूचनाएं दिखाएं"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"ऐप्लिकेशन को सूचनाएं दिखाने की अनुमति दें"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"बायोमीट्रिक हार्डवेयर इस्तेमाल करने दें"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"पुष्टि के लिए, ऐप्लिकेशन को बायोमीट्रिक हार्डवेयर इस्तेमाल करने की मंज़ूरी दें"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"फ़िंगरप्रिंट हार्डवेयर को प्रबंधित करें"</string>
diff --git a/core/res/res/values-hr-television/strings.xml b/core/res/res/values-hr-television/strings.xml
new file mode 100644
index 0000000..df5d8ea
--- /dev/null
+++ b/core/res/res/values-hr-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofon je blokiran"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera je blokirana"</string>
+</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 23b2a2f..04832f7 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -329,6 +329,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"uspostavljati telefonske pozive i upravljati njima"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Biometrijski senzori"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"pristupiti podacima senzora o vašim vitalnim znakovima"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Obavijesti"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"prikazati obavijesti"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Dohvaćati sadržaj prozora"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Pregledat će sadržaj prozora koji upotrebljavate."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Uključiti značajku Istraži dodirom"</string>
@@ -552,6 +554,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Aplikaciji omogućuje onemogućavanje zaključavanja tipkovnice i svih pripadajućih sigurnosnih zaporki. Na primjer, telefon onemogućuje zaključavanje tipkovnice kod primanja dolaznog telefonskog poziva, nakon kojeg se zaključavanje tipkovnice ponovo omogućuje."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"zahtijevati složenost zaključavanja zaslona"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Omogućuje aplikaciji da sazna razinu složenosti zaključavanja zaslona (visoka, srednja, niska ili nijedna), što upućuje na mogući raspon duljine i vrstu zaključavanja zaslona. Aplikacija također korisnicima može predložiti da ažuriraju zaključavanje zaslona na određenu razinu, no korisnici to mogu slobodno zanemariti i nastaviti dalje. Napominjemo da se zaključavanje zaslona ne pohranjuje u običnom tekstu, pa aplikacija ne zna točnu zaporku."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"prikazati obavijesti"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Aplikaciji omogućuje prikazivanje obavijesti"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"koristiti biometrijski hardver"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Aplikaciji omogućuje upotrebu biometrijskog hardvera radi autentifikacije"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"upravljanje hardverom za čitanje otisaka prstiju"</string>
diff --git a/core/res/res/values-hu-television/strings.xml b/core/res/res/values-hu-television/strings.xml
new file mode 100644
index 0000000..64583cd
--- /dev/null
+++ b/core/res/res/values-hu-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"A mikrofon le van tiltva"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"A kamera le van tiltva"</string>
+</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index e59d3c4..4a4e13d 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"telefonhívások kezdeményezése és kezelése"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Testérzékelők"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"az érzékelők által mért, életjelekkel kapcsolatos adatok elérése"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Értesítések"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"értesítések megjelenítése"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Ablaktartalom lekérdezése"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"A használt ablak tartalmának vizsgálata."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Felfedezés érintéssel bekapcsolása"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Lehetővé teszi az alkalmazás számára a billentyűzár és bármely kapcsolódó jelszavas védelem kikapcsolását. Például a telefon feloldja a billentyűzárat bejövő hívás esetén, majd újra bekapcsolja azt a hívás végeztével."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"képernyőzár összetettségi szintjének lekérése"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Lehetővé teszi az alkalmazás számára, hogy megismerje a képernyőzár összetettségi szintjét (magas, közepes, alacsony vagy nincs), amely jelzi a képernyőzár lehetséges típusát és hosszát. Az alkalmazás ezenkívül javaslatot tehet a felhasználóknak a képernyőzár bizonyos szintre való frissítésére, de ezt a javaslatot a felhasználók figyelmen kívül hagyhatják. A képernyőzárat nem egyszerű szöveges formátumban tárolja a rendszer, ezért az alkalmazás nem fogja tudni a pontos jelszót."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"értesítések megjelenítése"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Lehetővé teszi az alkalmazás számára az értesítések megjelenítését"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"biometrikus hardver használata"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Lehetővé teszi az alkalmazás számára a biometrikus hardver hitelesítésre való használatát"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"ujjlenyomat-olvasó hardver kezelése"</string>
diff --git a/core/res/res/values-hy-television/strings.xml b/core/res/res/values-hy-television/strings.xml
new file mode 100644
index 0000000..5652b44
--- /dev/null
+++ b/core/res/res/values-hy-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Խոսափողն արգելափակված է"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Տեսախցիկն արգելափակված է"</string>
+</resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 0a51e3e..389412c 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"կատարել զանգեր և կառավարել զանգերը"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Մարմնի տվիչներ"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"օգտագործել սենսորների տվյալները ձեր օրգանիզմի վիճակի մասին"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Ծանուցումներ"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"ցուցադրել ծանուցումներ"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Առբերել պատուհանի բովանդակությունը"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Վերլուծել գործող պատուհանի բովանդակությունը"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Միացնել Հպման միջոցով հետազոտումը"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Թույլ է տալիս հավելվածին անջատել ստեղնաշարի կողպումը և ցանկացած կապված գաղտնաբառի պաշտպանվածությունը: Սրա ճիշտ օրինակն է, երբ հեռախոսը անջատում է ստեղնաշարի կողպումը մուտքային զանգ ստանալիս, հետո այն կրկին միացնում է, երբ զանգը ավարտվում է:"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"հարցում էկրանի կողպման բարդության մակարդակի մասին"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Հավելվածին հասանելի կդառնան էկրանի կողպման բարդության մակարդակի մասին տեղեկությունները (բարձր, միջին, ցածր կամ ոչ մեկը), այդ թվում՝ կողպման տեսակի և գաղտնաբառի երկարության մասին տվյալները: Բացի այդ, հավելվածը կկարողանա առաջարկել օգտատերերին բարձրացնել կողպման բարդության մակարդակը: Օգտատերերը կարող են անտեսել այդ առաջարկները: Նկատի ունեցեք, որ գաղտնաբառը չի պահվում բաց տեքստի տեսքով և հասանելի չէ հավելվածին:"</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"ցուցադրել ծանուցումներ"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Թույլ է տալիս հավելվածին ցուցադրել ծանուցումներ"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"կենսաչափական սարքի օգտագործում"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Հավելվածին թույլ է տալիս օգտագործել նույնականացման համար նախատեսված կենսաչափական սարքը"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"կառավարել մատնահետքերի գրանցման սարքը"</string>
diff --git a/core/res/res/values-in-television/strings.xml b/core/res/res/values-in-television/strings.xml
new file mode 100644
index 0000000..4def907
--- /dev/null
+++ b/core/res/res/values-in-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofon diblokir"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera diblokir"</string>
+</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index b44342a..2ad3a55 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"melakukan dan mengelola panggilan telepon"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensor tubuh"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"mengakses data sensor tentang tanda-tanda vital"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notifikasi"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"tampilkan notifikasi"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Membaca konten di jendela"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Memeriksa konten di jendela yang sedang Anda buka."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Mengaktifkan Jelajahi dengan Sentuhan"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Memungkinkan aplikasi menonaktifkan kunci tombol dan keamanan sandi apa pun yang terkait. Misalnya, ponsel menonaktifkan kunci tombol saat menerima panggilan telepon masuk, kemudian mengaktifkan kembali kunci tombol ketika panggilan selesai."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"meminta kompleksitas kunci layar"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Mengizinkan aplikasi mempelajari tingkat kompleksitas kunci layar (tinggi, sedang, rendah, atau tidak ada), yang menunjukkan kemungkinan rentang durasi dan jenis kunci layar. Aplikasi juga dapat menyarankan agar pengguna memperbarui kunci layar ke tingkat tertentu, namun pengguna dapat mengabaikan dan keluar dengan bebas. Perhatikan bahwa kunci layar tidak disimpan dalam teks biasa, sehingga aplikasi tidak mengetahui sandi yang sebenarnya."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"tampilkan notifikasi"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Mengizinkan aplikasi untuk menampilkan notifikasi"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"gunakan hardware biometrik"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Mengizinkan aplikasi menggunakan hardware biometrik untuk autentikasi"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"kelola hardware sidik jari"</string>
diff --git a/core/res/res/values-is-television/strings.xml b/core/res/res/values-is-television/strings.xml
new file mode 100644
index 0000000..6451b40
--- /dev/null
+++ b/core/res/res/values-is-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Lokað er fyrir hljóðnemann"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Lokað er fyrir myndavélina"</string>
+</resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 7656f88..982b6c2 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"hringja og stjórna símtölum"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Líkamsskynjarar"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"aðgangur að skynjaragögnum yfir lífsmörk þín"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Tilkynningar"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"sýna tilkynningar"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Sækja innihald glugga"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Kanna innihald glugga sem þú ert að nota."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Kveikja á snertikönnun"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Leyfir forriti að slökkva á símalásnum og öðrum öryggisaðgerðum tengdum aðgangsorði. Til dæmis gerir síminn lásinn óvirkan þegar símtal berst og læsist svo aftur að símtali loknu."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"biðja um flókinn skjálás"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Leyfir forritinu að læra hversu flókinn skjálásinn er (erfiður, miðlungs, léttur eða enginn), sem gefur til kynna lengd og tegund skjálássins. Forritið getur einnig lagt til að notendur geri skjálásinn flóknari upp að tilteknu marki, en notendur geta valið að hunsa það og halda áfram að vafra. Hafðu í huga að skjálásinn er ekki geymdur í ódulkóðuðum texta svo forritið veit ekki nákvæmt aðgangsorð."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"sýna tilkynningar"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Leyfir forritinu að sýna tilkynningar"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"nota búnað fyrir líffræðileg gögn"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Leyfir forritinu að nota búnað fyrir líffræðileg gögn til auðkenningar"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"stjórna fingrafarabúnaði"</string>
diff --git a/core/res/res/values-it-television/strings.xml b/core/res/res/values-it-television/strings.xml
new file mode 100644
index 0000000..2125b30
--- /dev/null
+++ b/core/res/res/values-it-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Microfono bloccato"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Fotocamera bloccata"</string>
+</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 62f87a9..ad19a87 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"eseguire e gestire le telefonate"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensori del corpo"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"accedere ai dati dei sensori relativi ai tuoi parametri vitali"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notifiche"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"Visualizzazione di notifiche"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Recuperare contenuti della finestra"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Esamina i contenuti di una finestra con cui interagisci."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Attivare Esplora al tocco"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Consente all\'applicazione di disattivare il blocco tastiera ed eventuali protezioni tramite password associate. Ad esempio, il telefono disattiva il blocco tastiera quando riceve una telefonata in arrivo e lo riattiva al termine della chiamata."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"richiesta di complessità del blocco schermo"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Consente all\'app di conoscere il livello di complessità del blocco schermo (alto, medio, basso o nessuno), che indica l\'intervallo di caratteri possibile e il tipo di blocco schermo. L\'app può inoltre suggerire agli utenti di aggiornare il blocco schermo a un livello specifico di complessità, ma gli utenti possono ignorare liberamente il suggerimento e uscire. Tieni presente che il blocco schermo non viene memorizzato come testo non crittografato, quindi l\'app non conosce la password esatta."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"Visualizzazione di notifiche"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Consente all\'app di mostrare notifiche"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"utilizzo di hardware biometrico"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Consente all\'app di utilizzare hardware biometrico per eseguire l\'autenticazione"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"gestione di hardware per il riconoscimento delle impronte"</string>
diff --git a/core/res/res/values-iw-television/strings.xml b/core/res/res/values-iw-television/strings.xml
new file mode 100644
index 0000000..d228818
--- /dev/null
+++ b/core/res/res/values-iw-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"המיקרופון חסום"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"המצלמה חסומה"</string>
+</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 7131b19..3b55118 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -332,6 +332,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ביצוע וניהול של שיחות טלפון"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"חיישנים גופניים"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"גישה אל נתוני חיישנים של הסימנים החיוניים שלך"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"אחזור תוכן של חלון"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"בדיקת התוכן של חלון שאיתו מתבצעת אינטראקציה."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"הפעלה של \'גילוי באמצעות מגע\'"</string>
@@ -555,6 +559,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"מאפשרת לאפליקציה להשבית את נעילת המקשים וכל אמצעי אבטחה משויך המבוסס על סיסמה. לדוגמה, הטלפון ישבית את נעילת המקשים במהלך שיחת טלפון נכנסת, ויפעיל מחדש את נעילת המקשים עם סיום השיחה."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"בקשת מידע לגבי מידת המורכבות של נעילת המסך"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"מאפשרת לאפליקציה ללמוד את רמת המורכבות של נעילת המסך (גבוהה, בינונית, נמוכה או לא מורכבת). הרמה הזו מציינת את הטווח האפשרי של אורך וסוג נעילת המסך. האפליקציה יכולה גם להציע למשתמשים לעדכן את נעילת המסך לרמה מסוימת, אבל המשתמשים יכולים להתעלם מההצעה ולנווט לפריט אחר. לתשומת ליבך, נעילת המסך לא מאוחסנת כטקסט פשוט, ולכן האפליקציה לא יודעת מה הסיסמה המדויקת."</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"שימוש בחומרה ביומטרית"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"מאפשרת לאפליקציה להשתמש בחומרה ביומטרית לצורך אימות"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"ניהול חומרה של טביעות אצבעות"</string>
diff --git a/core/res/res/values-ja-television/strings.xml b/core/res/res/values-ja-television/strings.xml
new file mode 100644
index 0000000..a14d73f
--- /dev/null
+++ b/core/res/res/values-ja-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"マイクがブロックされています"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"カメラがブロックされています"</string>
+</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index df4b8bf..962b65b 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"電話の発信と管理"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"ボディセンサー"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"バイタルサインに関するセンサーデータへのアクセス"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"通知"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"通知の表示"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ウィンドウコンテンツの取得"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ユーザーがアクセスしているウィンドウのコンテンツを検査します。"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"タッチガイドの有効化"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"キーロックとキーロックに関連付けられたパスワードのセキュリティを無効にすることをアプリに許可します。たとえば、かかってきた電話を受ける際にキーロックを無効にし、通話が終了したらキーロックを再度有効にする場合などに使用します。"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"画面ロックの複雑さのリクエスト"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"このアプリに画面ロックの複雑さレベル(高、中、低、なし)を認識することを許可します。複雑さレベルは、画面ロックの文字数の範囲やタイプを示すものです。アプリから一定レベルまで画面ロックを更新するよう推奨されることもありますが、ユーザーは無視したり別の操作を行ったりできます。画面ロックは平文で保存されないため、アプリが正確なパスワードを知ることはありません。"</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"通知の表示"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"通知の表示をアプリに許可"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"生体認証ハードウェアの使用"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"生体認証ハードウェアを認証に使用することをアプリに許可します"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"指紋認証ハードウェアの管理"</string>
diff --git a/core/res/res/values-ka-television/strings.xml b/core/res/res/values-ka-television/strings.xml
new file mode 100644
index 0000000..2b8dce3
--- /dev/null
+++ b/core/res/res/values-ka-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"მიკროფონი დაბლოკილია"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"კამერა დაბლოკილია"</string>
+</resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 6e7bd85..bb307f9 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"სატელეფონო ზარების განხორციელება და მართვა"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"სხეულის სენსორები"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"თქვენი სასიცოცხლო ფუნქციების შესახებ სენსორის მონაცემებზე წვდომა"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"შეტყობინებები"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"შეტყობინებების ჩვენება"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ფანჯრის კონტენტის მოძიება"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"იმ ფანჯრის კონტენტის შემოწმება, რომელშიც მუშაობთ."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"„შეხებით აღმოჩენის“ ჩართვა"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"შეეძლება კლავიატურის დაბლოკვისა და პაროლით უზრუნველყოფილი ნებისმიერი უსაფრთხოების ფუნქციის დეაქტივაცია. მაგალითად, ტელეფონი შემომავალი ზარის დროს აუქმებს კლავიატურის დაბლოკვას და კვლავ ააქტიურებს მას, როგორც კი ზარი დასრულდება."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"ეკრანის დაბლოკვის მეთოდის სირთულის შესახებ ინფორმაციის მოთხოვნა"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"საშუალებას აძლევს აპს, შეიტყოს ეკრანის დაბლოკვის მეთოდის სირთულე (მაღალი, საშუალო, დაბალი ან არანაირი), რისი მეშვეობითაც შესაძლებელია ეკრანის დაბლოკვის მეთოდის სიგრძის შესაძლო დიაპაზონისა და ტიპის განსაზღვრა. გარდა ამისა, აპს შეუძლია მომხმარებლებისთვის ეკრანის დაბლოკვის მეთოდის გარკვეულ დონემდე გაძლიერების შეთავაზება, თუმცა მომხმარებლებს შეეძლებათ აღნიშნული შეტყობინების უგულებელყოფა და სხვა ეკრანზე გადასვლა. გაითვალისწინეთ, რომ ეკრანის დაბლოკვის მეთოდი არ ინახება ჩვეულებრივი ტექსტის სახით, ამიტომ აპს არ ეცოდინება ზუსტი პაროლი."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"შეტყობინებების ჩვენება"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"უფლებას აძლევს აპს, აჩვენოს შეტყობინებები"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"ბიომეტრიული აპარატის გამოყენება"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"საშუალებას აძლევს აპს, ავტორიზაციისთვის გამოიყენოს ბიომეტრიული აპარატი"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"თითის ანაბეჭდის აპარატის მართვა"</string>
diff --git a/core/res/res/values-kk-television/strings.xml b/core/res/res/values-kk-television/strings.xml
new file mode 100644
index 0000000..5730867
--- /dev/null
+++ b/core/res/res/values-kk-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Микрофон бөгелген"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Камера бөгелген"</string>
+</resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 8eb1069..1e84e78 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"қоңырау шалу және телефон қоңырауларын басқару"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Дене датчиктері"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"ағза күйінің көрсеткіштері туралы сенсор деректеріне қатынасу"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Хабарландырулар"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"хабарландыруларды көрсету"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Терезе мазмұнын оқып отыру"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Ашық тұрған терезе мазмұнын тексеру."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Түртілген элементтерді дыбыстау функциясын қосу"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Қолданбаларға кілтперне және басқа кілтсөзге қатысты қауіпсіздік шараларын өшіру мүмкіндігін береді. Мысалы, телефон кіріс қоңырауларын алғанда кілтпернені өшіреді және қоңырау аяқталғанда қайта қосады."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"экранды құлыптау күрделілігін сұрау"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Қолданбаға экран құлпының күрделілік деңгейін (жоғары, орташа, төмен немесе жоқ), соның ішінде ұзындығының ықтимал ауқымын және оның түрін анықтауға мүмкіндік береді. Сонымен қатар қолданба пайдаланушыларға құлыпты белгілі бір деңгейге жаңартуды ұсынады. Бірақ бұл ұсыныстарды елемеуге болады. Экран құлпы қарапайым мәтін түрінде сақталмайтындықтан, құпия сөз қолданбаға белгісіз болатынын ескеріңіз."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"хабарландыруларды көрсету"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Қолданбаға хабарландыруларды көрсетуге мүмкіндік береді."</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"биометрикалық жабдықты пайдалану"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Аутентификациялау үшін қолданбаға биометрикалық жабдықты пайдалануға рұқсат береді"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"саусақ ізі жабдығын басқару"</string>
diff --git a/core/res/res/values-km-television/strings.xml b/core/res/res/values-km-television/strings.xml
new file mode 100644
index 0000000..eb8b0e6
--- /dev/null
+++ b/core/res/res/values-km-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"មីក្រូហ្វូនត្រូវបានទប់ស្កាត់"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"កាមេរ៉ាត្រូវបានទប់ស្កាត់"</string>
+</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 9949e12..406e0a5 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ហៅទូរស័ព្ទ និងគ្រប់គ្រងការហៅទូរស័ព្ទ"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"ឧបករណ៍ចាប់សញ្ញារាងកាយ"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"ចូលដំណើរការទិន្នន័យឧបករណ៍ចាប់សញ្ញាអំពីស្ថានភាពសុខភាពរបស់អ្នក"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"ការជូនដំណឹង"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"បង្ហាញការជូនដំណឹង"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ទាញយកខ្លឹមសារវិនដូ"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ពិនិត្យខ្លឹមសារវិនដូដែលអ្នកកំពុងទាក់ទងជាមួយ។"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"បើកការរកមើលដោយប៉ះ"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"ឲ្យកម្មវិធីបិទការចាក់សោសុវត្ថិភាពពាក្យសម្ងាត់ដែលបានភ្ជាប់ណាមួយ។ ឧទាហរណ៍ត្រឹមត្រូវនៃការបិទទូរស័ព្ទពេលទទួលការហៅចូល បន្ទាប់មបើកសោពេលការហៅបានបញ្ចប់។"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"ស្នើសុំកម្រិតស្មុគស្មាញនៃការចាក់សោអេក្រង់"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"អនុញ្ញាតឱ្យកម្មវិធីរៀនអំពីកម្រិតស្មុគស្មាញការនៃការចាក់សោអេក្រង់ (ខ្ពស់ មធ្យម ទាប ឬគ្មាន) ដែលបញ្ជាក់អំពីប្រវែង និងប្រភេទនៃការចាក់សោអេក្រង់។ កម្មវិធីនេះក៏អាចណែនាំឱ្យអ្នកប្រើប្រាស់ធ្វើបច្ចុប្បន្នភាពការចាក់សោអេក្រង់ទៅកម្រិតជាក់លាក់ផងដែរ ប៉ុន្តែអ្នកប្រើប្រាស់អាចមិនអើពើនឹងការណែនាំនេះដោយសេរី។ សូមចំណាំថា ការចាក់សោអេក្រង់មិនត្រូវបានរក្សាទុកជាអត្ថបទធម្មតាទេ ដូច្នេះកម្មវិធីនេះមិនស្គាល់ពាក្យសម្ងាត់ពិតប្រាកដឡើយ។"</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"បង្ហាញការជូនដំណឹង"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"អនុញ្ញាតឱ្យកម្មវិធីបង្ហាញការជូនដំណឹង"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"ប្រើឧបករណ៍ស្កេនស្នាមម្រាមដៃ"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"អនុញ្ញាតឱ្យកម្មវិធីប្រើឧបករណ៍ស្កេនស្នាមម្រាមដៃសម្រាប់ការផ្ទៀងផ្ទាត់"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"គ្រប់គ្រងផ្នែករឹងស្នាមម្រាមដៃ"</string>
diff --git a/core/res/res/values-kn-television/strings.xml b/core/res/res/values-kn-television/strings.xml
new file mode 100644
index 0000000..5209477
--- /dev/null
+++ b/core/res/res/values-kn-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"ಮೈಕ್ರೋಫೋನ್ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"ಕ್ಯಾಮರಾ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ"</string>
+</resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 2d7df35..2a9a59b 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -326,6 +326,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ಫೋನ್ ಕರೆ ಮಾಡಲು ಹಾಗೂ ನಿರ್ವಹಿಸಲು"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"ಬಾಡಿ ಸೆನ್ಸರ್"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"ನಿಮ್ಮ ಮುಖ್ಯ ಲಕ್ಷಣಗಳ ಕುರಿತು ಸೆನ್ಸಾರ್ ಡೇಟಾವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ವಿಂಡೋ ವಿಷಯವನ್ನು ಹಿಂಪಡೆಯುತ್ತದೆ"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ನೀವು ಬಳಸುತ್ತಿರುವ ವಿಂಡೋದ ವಿಷಯ ಪರೀಕ್ಷಿಸುತ್ತದೆ."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"ಸ್ಪರ್ಶ-ಎಕ್ಸ್ಪ್ಲೋರ್ ಆನ್ ಮಾಡುತ್ತದೆ"</string>
@@ -549,6 +553,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"ಕೀಲಾಕ್ ಮತ್ತು ಯಾವುದೇ ಸಂಬಂಧಿತ ಭದ್ರತಾ ಪಾಸ್ವರ್ಡ್ ಭದ್ರತೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿ ನೀಡುತ್ತದೆ. ಉದಾಹರಣೆಗೆ, ಒಳಬರುವ ಕರೆಯನ್ನು ಸ್ವೀಕರಿಸುವಾಗ ಕೀಲಾಕ್ ಅನ್ನು ಫೋನ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ, ನಂತರ ಕರೆಯು ಅಂತ್ಯಗೊಂಡಾಗ ಕೀಲಾಕ್ ಅನ್ನು ಮರು ಸಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಸಂಕೀರ್ಣತೆಯನ್ನು ವಿನಂತಿಸಿ"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"ಆ್ಯಪ್ಗೆ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಸಂಕೀರ್ಣತೆ ಮಟ್ಟವನ್ನು (ಅಧಿಕ, ಮಧ್ಯಮ, ಕಡಿಮೆ ಅಥವಾ ಯಾವುದೂ ಅಲ್ಲ) ತಿಳಿದುಕೊಳ್ಳಲು ಅನುಮತಿಸುತ್ತದೆ, ಅದು ಉದ್ದದ ಸಂಭವನೀಯ ಶ್ರೇಣಿ ಮತ್ತು ಸ್ಕ್ರೀನ್ ಲಾಕ್ನ ವಿಧವನ್ನು ಸೂಚಿಸುತ್ತದೆ. ಬಳಕೆದಾರರು ನಿರ್ದಿಷ್ಟ ಹಂತದವರೆಗೆ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬಹುದು, ಆದರೆ ಅವರು ಅದನ್ನು ನಿರ್ಲಕ್ಷಿಸಬಹುದು ಮತ್ತು ಅದರಿಂದ ಹೊರಬರಬಹುದು ಎಂಬುದನ್ನು ಸಹ ಆ್ಯಪ್ ಬಳಕೆದಾರರಿಗೆ ಸೂಚಿಸುತ್ತದೆ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಪಠ್ಯದ ರೂಪದಲ್ಲಿ ಸಂಗ್ರಹಿಸಲಾಗಿರುವುದಿಲ್ಲ, ಇದರಿಂದಾಗಿ ಆ್ಯಪ್ಗೆ ಸರಿಯಾದ ಪಾಸ್ವರ್ಡ್ ಗೊತ್ತಿರುವುದಿಲ್ಲ ಎಂಬುದನ್ನು ಗಮನಿಸಿ."</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್ವೇರ್ ಬಳಸಿ"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಬಯೋಮೆಟ್ರಿಕ್ ಹಾರ್ಡ್ವೇರ್ ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಹಾರ್ಡ್ವೇರ್ ನಿರ್ವಹಿಸಿ"</string>
diff --git a/core/res/res/values-ko-television/strings.xml b/core/res/res/values-ko-television/strings.xml
new file mode 100644
index 0000000..93c58ee
--- /dev/null
+++ b/core/res/res/values-ko-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"마이크가 차단됨"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"카메라가 차단됨"</string>
+</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 04b17da..4e67b19 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"전화 걸기 및 관리"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"신체 센서"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"생체 신호에 관한 센서 데이터에 액세스"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"알림"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"알림 표시"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"창 콘텐츠 가져오기"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"상호작용 중인 창의 콘텐츠를 검사합니다."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"터치하여 탐색 사용"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"앱이 키 잠금 및 관련 비밀번호 보안을 사용중지할 수 있도록 허용합니다. 예를 들어, 휴대전화가 수신전화를 받을 때 키 잠금을 사용중지했다가 통화가 끝나면 키 잠금을 다시 사용할 수 있습니다."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"화면 잠금 복잡도 요청"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"앱이 화면 잠금 길이와 유형의 가능한 범위를 나타내는 잠금 화면 복잡도 수준(높음, 보통, 낮음 또는 없음)을 파악하도록 허용합니다. 앱이 사용자에게 화면 잠금을 특정 수준으로 업데이트할 것을 제안할 수도 있지만, 사용자는 자유롭게 이를 무시하고 다른 곳으로 이동할 수 있습니다. 화면 잠금은 일반 텍스트로 저장되지 않으므로 앱에서 정확한 비밀번호를 알 수 없습니다."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"알림 표시"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"앱에서 알림을 표시하도록 허용"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"생체 인식 하드웨어 사용"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"앱에서 생체 인식 하드웨어를 인증에 사용하도록 허용합니다."</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"지문 하드웨어 관리"</string>
diff --git a/core/res/res/values-ky-television/strings.xml b/core/res/res/values-ky-television/strings.xml
new file mode 100644
index 0000000..436daee
--- /dev/null
+++ b/core/res/res/values-ky-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Микрофон бөгөттөлгөн"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Камера бөгөттөлгөн"</string>
+</resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 1f869aa..0568133 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"телефон чалуу жана аларды башкаруу"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Дене сенсорлору"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"организмдин абалына көз салган сенсордун дайындарына мүмкүнчүлүк алуу"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Билдирмелер"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"билдирмелерди көрсөтүү"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Терезедеги мазмунду алып турат"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Учурда ачылып турган терезедеги маалыматты талдайт."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"\"Сыйпалап изилдөө\" мүмкүнчүлүгүн иштетет"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Колдонмого экрандын бөгөттөөчү жана ага байланыштуу сырсөз коргоосун өчүрүү уруксатын берет. Мисалы, чалуу келгенде экрандын бөгөтүн алып салат, чалуу бүткөндө кайрадан орнотот."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"экранды бөгөттөөнүн татаалдык деңгээлин суроо"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Колдонмого экранды бөгөттөөнүн татаалдыгын (татаал, орточо, оңой же такыр жок) үйрөнүүгө мүмкүнчүлүк берет. Татаалдык деңгээли сырсөздүн узундугу жана экранды бөгөттөөнүн түрү боюнча айырмаланат. Колдонмо экранды бөгөттөөнү белгилүү деңгээлге тууралоону колдонуучуларга сунуштай да алат, бирок колдонуучулар ага көңүл бурбай койсо болот. Сырсөздү колдонмо билбеши үчүн, экранды бөгөттөө сырсөзүн кадимки текстте сактоого болбойт."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"билдирмелерди көрсөтүү"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Колдонмолор билдирмелерди көрсөтө алат"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"биометрикалык аппаратты колдонуу"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Колдонмого аныктыгын текшерүү үчүн биометрикалык аппаратты пайдалануу мүмкүндүгүн берет"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"манжа изинин аппараттык камсыздоосун башкаруу"</string>
diff --git a/core/res/res/values-lo-television/strings.xml b/core/res/res/values-lo-television/strings.xml
new file mode 100644
index 0000000..bc98fb6
--- /dev/null
+++ b/core/res/res/values-lo-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"ໄມໂຄຣໂຟນຖືກບລັອກໄວ້"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"ກ້ອງຖ່າຍຮູບຖືກບລັອກໄວ້"</string>
+</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 7484246..cd611f7 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -326,6 +326,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ໂທ ແລະຈັດການການໂທລະສັບ"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"ເຊັນເຊີຮ່າງກາຍ"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"ເຂົ້າຫາຂໍ້ມູນເຊັນເຊີກ່ຽວກັບສັນຍານຊີບຂອງທ່ານ"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ດຶງຂໍ້ມູນເນື້ອຫາໃນໜ້າຈໍ"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ກວດກາເນື້ອຫາຂອງໜ້າຈໍທີ່ທ່ານກຳລັງມີປະຕິສຳພັນນຳ."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"ເປີດໃຊ້ \"ການສຳຫຼວດໂດຍສຳຜັດ\""</string>
@@ -549,6 +553,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"ອະນຸຍາດໃຫ້ແອັບຯປິດການເຮັດວຽກຂອງປຸ່ມລັອກ ແລະລະບົບຄວາມປອດໄພຂອງລະຫັດຜ່ານທີ່ເຊື່ອມໂຍງກັນ. ໂຕຢ່າງ: ໂທລະສັບຈະປິດການເຮັດວຽກຂອງປຸ່ມລັອກເມື່ອມີສາຍໂທເຂົ້າ ຈາກນັ້ນຈຶ່ງເປີດໃຊ້ໄດ້ອີກເມື່ອວາງສາຍແລ້ວ."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"ຮ້ອງຂໍຄວາມຊັບຊ້ອນການລັອກໜ້າຈໍ"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"ອະນຸຍາດໃຫ້ແອັບສຶກສາລະດັບຄວາມຊັບຊ້ອນຂອງໜ້າຈໍລັອກ (ສູງ, ກາງ ຫຼື ບໍ່ມີ), ເຊິ່ງລະບຸຂອບເຂດຄວາມເປັນໄປໄດ້ຂອງຄວາມຍາວ ແລະ ປະເພດຂອງການລັອກໜ້າຈໍ. ແອັບນີ້ສາມາດແນະນຳຜູ້ໃຊ້ວ່າເຂົາເຈົ້າສາມາດອັບເດດໜ້າຈໍລັອກເປັນລະດັບໃດໜຶ່ງເປັນການສະເພາະໄດ້, ແຕ່ຜູ້ໃຊ້ສາມາດທີ່ຈະບໍ່ສົນໃຈ ຫຼື ເປີດໄປອັນອື່ນໄດ້. ກະລຸນາຮັບຊາບວ່າການລັອກໜ້າຈໍບໍ່ໄດ້ບັນທຶກໃນແບບຂໍ້ຄວາມທຳມະດາ, ດັ່ງນັ້ນແອັບຈະບໍ່ຮູ້ລະຫັດຜ່ານທີ່ແນ່ນອນ."</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"ໃຊ້ຮາດແວຊີວະມິຕິ"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"ອະນຸຍາດໃຫ້ແອັບນຳໃຊ້ຮາດແວຊີວະມິຕິສຳລັບການພິສູດຢືນຢັນ"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"ຈັດການຮາດແວລາຍນີ້ວມື"</string>
diff --git a/core/res/res/values-lt-television/strings.xml b/core/res/res/values-lt-television/strings.xml
new file mode 100644
index 0000000..afe7a32
--- /dev/null
+++ b/core/res/res/values-lt-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofonas užblokuotas"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Fotoaparatas užblokuotas"</string>
+</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index eb5f2a6..aa70bd2 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -332,6 +332,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"skambinti ir tvarkyti telefonų skambučius"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Kūno jutikliai"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"pasiekti jutiklių duomenis apie gyvybinius ženklus"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Pranešimai"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"rodyti pranešimus"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Gauti lango turinį"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Tikrinti lango, su kuriuo sąveikaujate, turinį."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Įjungti „Naršyti paliečiant“"</string>
@@ -555,6 +557,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Leidžiama programai neleisti klavišo užrakto ir visos susijusios slaptažodžio apsaugos. Pvz., telefonas neleidžia klavišo užrakto priimant gaunamąjį skambutį ir pakartotinai jį įgalina, kai skambutis baigiamas."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"pateikti ekrano užrakto sudėtingumo užklausą"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Leidžiama programai sužinoti ekrano užrakto sudėtingumo lygį (aukštas, vidutinis, žemas arba nėra), nurodantį galimą ekrano užrakto trukmės diapazoną ir tipą. Be to, programa gali pasiūlyti naudotojams atnaujinti ekrano užraktą į tam tikrą lygį, bet naudotojai gali laisvai nepaisyti ir išeiti. Atminkite, kad ekrano užraktas nesaugomas kaip grynasis tekstas, todėl programa nežino tikslaus slaptažodžio."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"rodyti pranešimus"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Programai leidžiama rodyti pranešimus"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"naudoti biometrinę aparatinę įrangą"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Leidžiama programai naudoti biometrinę aparatinę įrangą tapatybei nustatyti"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"tvarkyti piršto antspaudo aparatinę įrangą"</string>
diff --git a/core/res/res/values-lv-television/strings.xml b/core/res/res/values-lv-television/strings.xml
new file mode 100644
index 0000000..c53a85f
--- /dev/null
+++ b/core/res/res/values-lv-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofons ir bloķēts"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera ir bloķēta"</string>
+</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index dac2104..b36cf76 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -329,6 +329,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"veikt un pārvaldīt tālruņa zvanus"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Ķermeņa sensori"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"piekļūt sensoru datiem par jūsu veselības rādījumiem"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Paziņojumi"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"rādīt paziņojumus"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Izgūt loga saturu."</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Skatīt tā loga saturu, ar kuru mijiedarbojaties."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Aktivizēt funkciju “Pārlūkot pieskaroties”."</string>
@@ -552,6 +554,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Ļauj lietotnei atspējot taustiņslēgu un visu saistīto paroļu drošību. Piemēram, tālrunis atspējo taustiņslēgu, saņemot ienākošu zvanu, un pēc zvana pabeigšanas atkārtoti iespējo taustiņslēgu."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"ekrāna bloķēšanas sarežģītības pakāpes informācijas pieprasījums"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Atļauj lietotnei saglabāt informāciju par ekrāna bloķēšanas sarežģītības pakāpi (augsta, vidēja, zema, nav), kas apzīmē iespējamo garumu un ekrāna bloķēšanas veidus. Lietotnē lietotājiem var tikt rādīts arī ieteikums ekrāna bloķēšanai iestatīt citu līmeni, taču šo ieteikumu var ignorēt un aizvērt. Ņemiet vērā, ka ekrāna bloķēšanas parole netiek glabāta vienkārša teksta formātā, tāpēc lietotnei nav piekļuves precīzai parolei."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"Paziņojumu rādīšana"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Ļauj lietotnei rādīt paziņojumus."</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"izmantot biometrisko datu aparatūru"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Atļauj lietotnei izmantot biometrisko datu aparatūru autentificēšanai"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"pārvaldīt pirkstu nospiedumu aparatūru"</string>
diff --git a/core/res/res/values-mcc334-mnc020-af/strings.xml b/core/res/res/values-mcc334-mnc020-af/strings.xml
new file mode 100644
index 0000000..c9177f3
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-af/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"STAWINGMISLUKKING -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NIE INGETEKEN OP DIENS NIE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Veelvuldige PDN-verbindings vir \'n bepaalde APN word nie toegelaat nie -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-am/strings.xml b/core/res/res/values-mcc334-mnc020-am/strings.xml
new file mode 100644
index 0000000..609fcf9
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-am/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"የማረጋገጫ አለመሳካት -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ለአገልግሎት ደንበኝነት አልተመዘገበም -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ለተሰጠው ኤፒኤን ብዙ የፒዲኤን ግንኙነቶች አይፈቀዱም -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ar/strings.xml b/core/res/res/values-mcc334-mnc020-ar/strings.xml
new file mode 100644
index 0000000..ac7dec0
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ar/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"تعذّرت المصادقة -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"غير مشترك في الخدمة -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"غير مسموح باتصالات PDN متعددة لاسم نقطة وصول محددة (APN) -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-as/strings.xml b/core/res/res/values-mcc334-mnc020-as/strings.xml
new file mode 100644
index 0000000..25b074e
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-as/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <!-- no translation found for config_pdp_reject_user_authentication_failed (4683454131283459978) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_service_not_subscribed (9021140729932308119) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_multi_conn_to_same_pdn_not_allowed (3838388706348367865) -->
+ <skip />
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-az/strings.xml b/core/res/res/values-mcc334-mnc020-az/strings.xml
new file mode 100644
index 0000000..83b210d
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-az/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"DOĞRULAMA XƏTASI -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"XİDMƏTƏ ABUNƏ OLUNMAYIB -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Verilmiş APN üçün birdən çox PDN bağlantısına icazə verilmir -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-b+sr+Latn/strings.xml b/core/res/res/values-mcc334-mnc020-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..b27e485
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-b+sr+Latn/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"POTVRDA IDENTITETA NIJE USPELA -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NISTE PRETPLAĆENI NA USLUGU -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Nije dozvoljeno više PDN veza za određeni APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-be/strings.xml b/core/res/res/values-mcc334-mnc020-be/strings.xml
new file mode 100644
index 0000000..3ee9e8f
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-be/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ЗБОЙ АЎТЭНТЫФІКАЦЫІ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"У ВАС НЯМА ПАДПІСКІ НА СЭРВІС -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Да гэтага APN нельга падключаць некалькі сетак PDN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-bg/strings.xml b/core/res/res/values-mcc334-mnc020-bg/strings.xml
new file mode 100644
index 0000000..257cf58
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-bg/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"НЕУСПЕШНО УДОСТОВЕРЯВАНЕ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"НЯМА АБОНАМЕНТ ЗА УСЛУГАТА -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Не е разрешено да има повече от една PDN връзка за дадено име на точката за достъп (APN) -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-bn/strings.xml b/core/res/res/values-mcc334-mnc020-bn/strings.xml
new file mode 100644
index 0000000..25b074e
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-bn/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <!-- no translation found for config_pdp_reject_user_authentication_failed (4683454131283459978) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_service_not_subscribed (9021140729932308119) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_multi_conn_to_same_pdn_not_allowed (3838388706348367865) -->
+ <skip />
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-bs/strings.xml b/core/res/res/values-mcc334-mnc020-bs/strings.xml
new file mode 100644
index 0000000..4bf1b8f
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-bs/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTIFIKACIJA NIJE USPJELA -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NISTE PRETPLAĆENI NA USLUGU -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Više PDN veza za određeni APN nije dozvoljeno -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ca/strings.xml b/core/res/res/values-mcc334-mnc020-ca/strings.xml
new file mode 100644
index 0000000..0ad1f1c
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ca/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ERROR D\'AUTENTICACIÓ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NO ESTÀS SUBSCRIT AL SERVEI -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"No es permeten diverses connexions PDN per a un APN determinat -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-cs/strings.xml b/core/res/res/values-mcc334-mnc020-cs/strings.xml
new file mode 100644
index 0000000..cfa1a44
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-cs/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"SELHÁNÍ OVĚŘENÍ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NEJSTE PŘIHLÁŠENI K ODBĚRU SLUŽBY -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"U jednoho APN není povoleno více než jedno připojení PDN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-da/strings.xml b/core/res/res/values-mcc334-mnc020-da/strings.xml
new file mode 100644
index 0000000..7d349cf
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-da/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"GODKENDELSESFEJL -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"IKKE TILMELDT TJENESTEN -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Flere PDN-forbindelser for en given APN er ikke tilladt -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-de/strings.xml b/core/res/res/values-mcc334-mnc020-de/strings.xml
new file mode 100644
index 0000000..084bf46
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-de/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTHENTIFIZIERUNGSFEHLER -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"DIENST NICHT ABONNIERT -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Mehrere PDN-Verbindungen für einen bestimmten APN sind nicht zulässig -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-el/strings.xml b/core/res/res/values-mcc334-mnc020-el/strings.xml
new file mode 100644
index 0000000..27c5f95
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-el/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ΑΠΟΤΥΧΙΑ ΕΛΕΓΧΟΥ ΤΑΥΤΟΤΗΤΑΣ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ΔΕΝ ΕΓΙΝΕ ΕΓΓΡΑΦΗ ΣΤΗΝ ΥΠΗΡΕΣΙΑ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Δεν επιτρέπονται πολλές συνδέσεις PDN για ένα δεδομένο APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-en-rAU/strings.xml b/core/res/res/values-mcc334-mnc020-en-rAU/strings.xml
new file mode 100644
index 0000000..6dce345
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-en-rAU/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTHENTICATION FAILURE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NOT SUBSCRIBED TO SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Multiple PDN connections for a given APN not allowed -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-en-rCA/strings.xml b/core/res/res/values-mcc334-mnc020-en-rCA/strings.xml
new file mode 100644
index 0000000..6dce345
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-en-rCA/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTHENTICATION FAILURE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NOT SUBSCRIBED TO SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Multiple PDN connections for a given APN not allowed -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-en-rGB/strings.xml b/core/res/res/values-mcc334-mnc020-en-rGB/strings.xml
new file mode 100644
index 0000000..6dce345
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-en-rGB/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTHENTICATION FAILURE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NOT SUBSCRIBED TO SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Multiple PDN connections for a given APN not allowed -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-en-rIN/strings.xml b/core/res/res/values-mcc334-mnc020-en-rIN/strings.xml
new file mode 100644
index 0000000..6dce345
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-en-rIN/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTHENTICATION FAILURE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NOT SUBSCRIBED TO SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Multiple PDN connections for a given APN not allowed -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-en-rXC/strings.xml b/core/res/res/values-mcc334-mnc020-en-rXC/strings.xml
new file mode 100644
index 0000000..ec8f4ab
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-en-rXC/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTHENTICATION FAILURE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NOT SUBSCRIBED TO SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Multiple PDN connections for a given APN not allowed -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-es-rUS/strings.xml b/core/res/res/values-mcc334-mnc020-es-rUS/strings.xml
new file mode 100644
index 0000000..8cb181d
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-es-rUS/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"FALLO EN LA AUTENTICACIÓN -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NO TE SUSCRIBISTE AL SERVICIO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"No se permiten varias conexiones PDN para un APN determinado -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-es/strings.xml b/core/res/res/values-mcc334-mnc020-es/strings.xml
new file mode 100644
index 0000000..0744699
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-es/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ERROR DE AUTENTICACIÓN -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NO SUSCRITO AL SERVICIO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"No se permiten varias conexiones PDN de un APN concreto -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-et/strings.xml b/core/res/res/values-mcc334-mnc020-et/strings.xml
new file mode 100644
index 0000000..a7e9e16
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-et/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTIMINE EBAÕNNESTUS -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"TEENUST POLE TELLITUD -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Antud APN-i puhul pole mitu PDN-ühendust lubatud -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-eu/strings.xml b/core/res/res/values-mcc334-mnc020-eu/strings.xml
new file mode 100644
index 0000000..2d05b3f
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-eu/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"EZIN IZAN DA AUTENTIFIKATU -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"EZ DAGO HARPIDETUTA ZERBITZURA -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Ezin da konektatu APN jakin bat PDN batera baino gehiagotara -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-fa/strings.xml b/core/res/res/values-mcc334-mnc020-fa/strings.xml
new file mode 100644
index 0000000..8bf364f
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-fa/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"اصالتسنجی انجام نشد -۲۹-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"مشترک سرویس نیستید -۳۳-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"تعدادی از اتصالهای PDN مربوط به APN تعیینشده مجاز نیست -۵۵-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-fi/strings.xml b/core/res/res/values-mcc334-mnc020-fi/strings.xml
new file mode 100644
index 0000000..f8d01ba
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-fi/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"TODENNUS EPÄONNISTUI -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"PALVELUA EI TILATTU -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Useat yhteydet eivät ole sallittuja yhdelle APN:lle -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-fr-rCA/strings.xml b/core/res/res/values-mcc334-mnc020-fr-rCA/strings.xml
new file mode 100644
index 0000000..25b074e
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-fr-rCA/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <!-- no translation found for config_pdp_reject_user_authentication_failed (4683454131283459978) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_service_not_subscribed (9021140729932308119) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_multi_conn_to_same_pdn_not_allowed (3838388706348367865) -->
+ <skip />
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-fr/strings.xml b/core/res/res/values-mcc334-mnc020-fr/strings.xml
new file mode 100644
index 0000000..25b074e
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-fr/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <!-- no translation found for config_pdp_reject_user_authentication_failed (4683454131283459978) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_service_not_subscribed (9021140729932308119) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_multi_conn_to_same_pdn_not_allowed (3838388706348367865) -->
+ <skip />
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-gl/strings.xml b/core/res/res/values-mcc334-mnc020-gl/strings.xml
new file mode 100644
index 0000000..49baed9
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-gl/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ERRO DE AUTENTICACIÓN -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"SEN SUBSCRICIÓN AO SERVIZO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Non está permitido establecer varias conexións de tipo PDN para un determinado APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-gu/strings.xml b/core/res/res/values-mcc334-mnc020-gu/strings.xml
new file mode 100644
index 0000000..25b074e
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-gu/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <!-- no translation found for config_pdp_reject_user_authentication_failed (4683454131283459978) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_service_not_subscribed (9021140729932308119) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_multi_conn_to_same_pdn_not_allowed (3838388706348367865) -->
+ <skip />
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-hi/strings.xml b/core/res/res/values-mcc334-mnc020-hi/strings.xml
new file mode 100644
index 0000000..8a11053
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-hi/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"पुष्टि नहीं की जा सकी -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"सेवा की सदस्यता नहीं ली गई है -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"किसी भी एपीएन के लिए एक से ज़्यादा पीडीएन कनेक्शन की अनुमति नहीं है -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-hr/strings.xml b/core/res/res/values-mcc334-mnc020-hr/strings.xml
new file mode 100644
index 0000000..ff9b427
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-hr/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTIFIKACIJA NIJE USPJELA -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NISTE PRETPLAĆENI NA USLUGU -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Više PDN veza za određeni APN nije dopušteno -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-hu/strings.xml b/core/res/res/values-mcc334-mnc020-hu/strings.xml
new file mode 100644
index 0000000..42776c1
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-hu/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"SIKERTELEN HITELESÍTÉS -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NINCS ELŐFIZETÉSE A SZOLGÁLTATÁSRA -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Nem engedélyezett több PDN-kapcsolat egy adott APN-nél -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-hy/strings.xml b/core/res/res/values-mcc334-mnc020-hy/strings.xml
new file mode 100644
index 0000000..6633bb3
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-hy/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ՆՈՒՅՆԱԿԱՆԱՑՄԱՆ ՍԽԱԼ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ԴՈՒՔ ԲԱԺԱՆՈՐԴԱԳՐՎԱԾ ՉԵՔ ԱՅՍ ԾԱՌԱՅՈՒԹՅԱՆԸ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Այս APN-ով չի թույլատրվում միանալ տվյալների փոխանցման մեկից ավել բաց ցանցի -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-in/strings.xml b/core/res/res/values-mcc334-mnc020-in/strings.xml
new file mode 100644
index 0000000..79a9dfd
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-in/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"KEGAGALAN AUTENTIKASI -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"TIDAK BERLANGGANAN KE LAYANAN -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Beberapa koneksi PDN untuk APN tertentu tidak diizinkan -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-is/strings.xml b/core/res/res/values-mcc334-mnc020-is/strings.xml
new file mode 100644
index 0000000..c9049e8
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-is/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUÐKENNING MISTÓKST -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"EKKI ÁSKRIFANDI AÐ ÞJÓNUSTU -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Margar PDN-tengingar fyrir tiltekinn aðgangsstað eru ekki leyfðar -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-it/strings.xml b/core/res/res/values-mcc334-mnc020-it/strings.xml
new file mode 100644
index 0000000..add9b0d
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-it/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ERRORE DI AUTENTICAZIONE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"SOTTOSCRIZIONE AL SERVIZIO NON EFFETTUATA -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Più connessioni PDN per un dato APN non consentite -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-iw/strings.xml b/core/res/res/values-mcc334-mnc020-iw/strings.xml
new file mode 100644
index 0000000..b8b1ab5
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-iw/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"האימות נכשל -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"לא נרשמת כמנוי לשירות -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"אין הרשאה למספר חיבורי PDN ל-APN מסוים -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ja/strings.xml b/core/res/res/values-mcc334-mnc020-ja/strings.xml
new file mode 100644
index 0000000..ec543fa
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ja/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"認証エラーが発生しました -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"サービスに登録していません -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"特定の APN に対する複数の PDN 接続は許可されていません -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ka/strings.xml b/core/res/res/values-mcc334-mnc020-ka/strings.xml
new file mode 100644
index 0000000..e55057b
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ka/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ავტორიზაციის შეცდომა -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"გამოწერილი არ გაქვთ სერვისი -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"მრავალი PDN კავშირი მოცემული APN-ისთვის დაუშვებელია -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-kk/strings.xml b/core/res/res/values-mcc334-mnc020-kk/strings.xml
new file mode 100644
index 0000000..f19d2c7
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-kk/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"АУТЕНТИФИКАЦИЯЛАУ ҚАТЕСІ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ҚЫЗМЕТКЕ ЖАЗЫЛМАҒАН -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Бұл APN үшін бірнеше PDN-ге қосылуға рұқсат берілмеген -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-km/strings.xml b/core/res/res/values-mcc334-mnc020-km/strings.xml
new file mode 100644
index 0000000..08e1a0d
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-km/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"មិនអាចផ្ទៀងផ្ទាត់បានទេ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"មិនបានជាវសេវាកម្មទេ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ការតភ្ជាប់ PDN ច្រើនសម្រាប់ APN ដែលបានផ្ដល់មិនត្រូវបានអនុញ្ញាតទេ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-kn/strings.xml b/core/res/res/values-mcc334-mnc020-kn/strings.xml
new file mode 100644
index 0000000..e5780b3
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-kn/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ದೃಢೀಕರಣ ವಿಫಲಗೊಂಡಿದೆ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ಸೇವೆಗೆ ಸಬ್ಸ್ಕ್ರೈಬ್ ಮಾಡಿಲ್ಲ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ನೀಡಲಾದ APN ಗಾಗಿ ಬಹು PDN ಸಂಪರ್ಕಗಳನ್ನು ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ko/strings.xml b/core/res/res/values-mcc334-mnc020-ko/strings.xml
new file mode 100644
index 0000000..444d050
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ko/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"인증할 수 없음 -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"서비스 구독 중이 아님 -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"지정된 APN에 여러 PDN을 연결할 수 없음 -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ky/strings.xml b/core/res/res/values-mcc334-mnc020-ky/strings.xml
new file mode 100644
index 0000000..240c6de
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ky/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"АНЫКТЫГЫ ТЕКШЕРИЛГЕН ЖОК -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"КЫЗМАТКА ЖАЗЫЛГАН ЭМЕССИЗ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Бул APN менен бир нече PDN\'ге туташууга болбойт -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-lo/strings.xml b/core/res/res/values-mcc334-mnc020-lo/strings.xml
new file mode 100644
index 0000000..89eb9fc
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-lo/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ການພິສູດຢືນຢັນບໍ່ສຳເລັດ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ບໍ່ໄດ້ສະໝັກໃຊ້ບໍລິການ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ບໍ່ອະນຸຍາດການເຊື່ອມຕໍ່ PDN ຫຼາຍອັນສຳລັບ APN ທີ່ລະບຸ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-lt/strings.xml b/core/res/res/values-mcc334-mnc020-lt/strings.xml
new file mode 100644
index 0000000..9157b8d
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-lt/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTIFIKAVIMO TRIKTIS -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"PASLAUGA NEPRENUMERUOJAMA -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Kelių PDN ryšiai su APN neleidžiami -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-lv/strings.xml b/core/res/res/values-mcc334-mnc020-lv/strings.xml
new file mode 100644
index 0000000..33af863
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-lv/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTIFIKĀCIJAS KĻŪME -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"PAKALPOJUMS NAV ABONĒTS -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Norādītajam APN nav atļauti vairāki PDN savienojumi -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-mk/strings.xml b/core/res/res/values-mcc334-mnc020-mk/strings.xml
new file mode 100644
index 0000000..5b6b621
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-mk/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"НЕУСПЕШНА ПРОВЕРКА -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"НЕ СТЕ ПРЕТПЛАТЕНИ НА УСЛУГАТА -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Не се дозволени повеќекратни PDN-врски за дадена APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ml/strings.xml b/core/res/res/values-mcc334-mnc020-ml/strings.xml
new file mode 100644
index 0000000..d56b4c8
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ml/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"പരിശോധിച്ചുറപ്പിക്കൽ പരാജയം -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"സേവനം സബ്സ്ക്രൈബ് ചെയ്തിട്ടില്ല -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"നൽകിയിരിക്കുന്ന ഒരു APN-ൽ ഒന്നിലധികം PDN കണക്ഷനുകൾ അനുവദനീയമല്ല -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-mn/strings.xml b/core/res/res/values-mcc334-mnc020-mn/strings.xml
new file mode 100644
index 0000000..9eb4753
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-mn/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"БАТАЛГААЖУУЛЖ ЧАДСАНГҮЙ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ҮЙЛЧИЛГЭЭГ ЗАХИАЛААГҮЙ БАЙНА -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Өгсөн APN-д хэд хэдэн PDN холболтыг зөвшөөрдөггүй -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-mr/strings.xml b/core/res/res/values-mcc334-mnc020-mr/strings.xml
new file mode 100644
index 0000000..7adf666
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-mr/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ऑथेंटिकेशन यशस्वी झाले नाही -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"सेवेचे सदस्यत्व घेतलेले नाही -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"दिलेल्या APN साठी एकपेक्षा जास्त PDN कनेक्शनची अनुमती नाही -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ms/strings.xml b/core/res/res/values-mcc334-mnc020-ms/strings.xml
new file mode 100644
index 0000000..cbae67e
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ms/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"KEGAGALAN PENGESAHAN -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"TIDAK MELANGGAN PERKHIDMATAN -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Sambungan berbilang PDN untuk APN yang diberikan tidak dibenarkan -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-my/strings.xml b/core/res/res/values-mcc334-mnc020-my/strings.xml
new file mode 100644
index 0000000..f1e7d6e
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-my/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"အထောက်အထားစိစစ်ခြင်း မအောင်မြင်ပါ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ဝန်ဆောင်မှုကို စာရင်းသွင်းမထားပါ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ပေးထားသော APN အတွက် PDN ချိတ်ဆက်မှုအများအပြားကို ခွင့်မပြုပါ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-nb/strings.xml b/core/res/res/values-mcc334-mnc020-nb/strings.xml
new file mode 100644
index 0000000..623f2d9
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-nb/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTISERINGSFEIL -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ABONNERER IKKE PÅ TJENESTEN -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Flere PDN-tilkoblinger for et gitt APN er ikke tillatt -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ne/strings.xml b/core/res/res/values-mcc334-mnc020-ne/strings.xml
new file mode 100644
index 0000000..fd69276
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ne/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"पुष्टि गर्न सकिएन -२९-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"सेवाको सदस्यता लिइएको छैन -३३-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"कुनै निश्चित APN का हकमा एकभन्दा बढी PDN कनेक्सन गर्ने अनुमति दिइएको छैन -५५-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-nl/strings.xml b/core/res/res/values-mcc334-mnc020-nl/strings.xml
new file mode 100644
index 0000000..40cb2fa
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-nl/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"VERIFICATIE MISLUKT -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NIET GEABONNEERD OP SERVICE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Meerdere PDN-verbindingen voor een bepaalde APN niet toegestaan -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-or/strings.xml b/core/res/res/values-mcc334-mnc020-or/strings.xml
new file mode 100644
index 0000000..49810d5
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-or/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ପ୍ରାମାଣିକତା ବିଫଳ ହୋଇଛି -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ସେବା ପାଇଁ ସଦସ୍ୟତା ନିଆଯାଇନାହିଁ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ଦିଆଯାଇଥିବା ଏକ APN ପାଇଁ ଏକାଧିକ PDN ସଂଯୋଗକୁ ଅନୁମତି ଦିଆଯାଇନାହିଁ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-pa/strings.xml b/core/res/res/values-mcc334-mnc020-pa/strings.xml
new file mode 100644
index 0000000..3cf6bc8
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-pa/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ਪ੍ਰਮਾਣੀਕਰਨ ਅਸਫਲ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ਸੇਵਾ ਦੀ ਗਾਹਕੀ ਨਹੀਂ ਲਈ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ਕਿਸੇ ਵੀ APN ਲਈ ਇੱਕ ਤੋਂ ਵੱਧ PDN ਕਨੈਕਸ਼ਨਾਂ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-pl/strings.xml b/core/res/res/values-mcc334-mnc020-pl/strings.xml
new file mode 100644
index 0000000..9c5fd78
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-pl/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"BŁĄD UWIERZYTELNIANIA -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"USŁUGA NIESUBSKRYBOWANA -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Wielokrotne połączenia PDN w danym APN nie są dozwolone -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-pt-rBR/strings.xml b/core/res/res/values-mcc334-mnc020-pt-rBR/strings.xml
new file mode 100644
index 0000000..1292978
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-pt-rBR/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"FALHA NA AUTENTICAÇÃO -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"A CONTA NÃO ESTÁ INSCRITA NO SERVIÇO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Não é permitido ter várias conexões PDN para um determinado APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-pt-rPT/strings.xml b/core/res/res/values-mcc334-mnc020-pt-rPT/strings.xml
new file mode 100644
index 0000000..68248f3
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-pt-rPT/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"FALHA NA AUTENTICAÇÃO -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NÃO SUBSCREVEU O SERVIÇO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Não são permitidas várias ligações PDN para um determinado APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-pt/strings.xml b/core/res/res/values-mcc334-mnc020-pt/strings.xml
new file mode 100644
index 0000000..1292978
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-pt/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"FALHA NA AUTENTICAÇÃO -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"A CONTA NÃO ESTÁ INSCRITA NO SERVIÇO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Não é permitido ter várias conexões PDN para um determinado APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ro/strings.xml b/core/res/res/values-mcc334-mnc020-ro/strings.xml
new file mode 100644
index 0000000..92564e9
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ro/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"EROARE DE AUTENTIFICARE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"FĂRĂ ABONAMENT LA SERVICIU -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Nu se permit mai multe conexiuni PDN pentru un anumit APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ru/strings.xml b/core/res/res/values-mcc334-mnc020-ru/strings.xml
new file mode 100644
index 0000000..f4647ba
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ru/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ОШИБКА АУТЕНТИФИКАЦИИ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"НЕТ ПОДПИСКИ НА СЕРВИС -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Указанная точка доступа не поддерживает несколько PDN-соединений -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-si/strings.xml b/core/res/res/values-mcc334-mnc020-si/strings.xml
new file mode 100644
index 0000000..cb83975
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-si/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"සත්යාපනය අසාර්ථකයි -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"සේවාවකට ග්රාහක වී නැත -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"දී ඇති APN එකක් සඳහා බහුවිධ PDN සම්බන්ධතා ඉඩ නොදේ -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-sk/strings.xml b/core/res/res/values-mcc334-mnc020-sk/strings.xml
new file mode 100644
index 0000000..77efe30
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-sk/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"NEPODARILO SA OVERIŤ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"SLUŽBU NEODOBERÁTE -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Pre daný názov prístupového bodu (APN) nie je povolených viacero pripojení PDN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-sl/strings.xml b/core/res/res/values-mcc334-mnc020-sl/strings.xml
new file mode 100644
index 0000000..118c3dd
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-sl/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"NAPAKA PRI PREVERJANJU PRISTNOSTI. -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NISTE NAROČENI NA STORITEV. -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Za dano ime dostopne točke (APN) ni dovoljenih več povezav PDN. -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-sq/strings.xml b/core/res/res/values-mcc334-mnc020-sq/strings.xml
new file mode 100644
index 0000000..315a8ab
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-sq/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"DËSHTIM NË VERIFIKIM -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"NUK JE ABONUAR NË SHËRBIM -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Disa lidhje PDN për një APN të caktuar nuk lejohen -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-sr/strings.xml b/core/res/res/values-mcc334-mnc020-sr/strings.xml
new file mode 100644
index 0000000..ddf0ce1
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-sr/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ПОТВРДА ИДЕНТИТЕТА НИЈЕ УСПЕЛА -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"НИСТЕ ПРЕТПЛАЋЕНИ НА УСЛУГУ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Није дозвољено више PDN веза за одређени APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-sv/strings.xml b/core/res/res/values-mcc334-mnc020-sv/strings.xml
new file mode 100644
index 0000000..75bc642
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-sv/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"AUTENTISERINGSFEL -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"PRENUMERERAR INTE PÅ TJÄNSTEN -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Flera PDN-anslutningar för samma APN tillåts inte -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-sw/strings.xml b/core/res/res/values-mcc334-mnc020-sw/strings.xml
new file mode 100644
index 0000000..275f628
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-sw/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"IMESHINDWA KUTHIBITISHA -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"HUJAJISAJILI KWENYE HUDUMA -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Miunganisho mingi ya PDN kwenye APN husika hairuhusiwi -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ta/strings.xml b/core/res/res/values-mcc334-mnc020-ta/strings.xml
new file mode 100644
index 0000000..699ea71
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ta/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"அங்கீகரிக்க முடியவில்லை -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"சேவைக்குச் சந்தா பெறவில்லை -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ஒரு APNக்குப் பல PDN இணைப்புகள் அனுமதிக்கப்படாது -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-te/strings.xml b/core/res/res/values-mcc334-mnc020-te/strings.xml
new file mode 100644
index 0000000..eee21aa
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-te/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ప్రామాణీకరణ వైఫల్యం -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ఈ సర్వీస్ కోసం సబ్స్క్రయిబ్ చేసుకోలేదు -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ఈ APNలో బహుళ PDN కనెక్షన్లు అనుమతించబడవు -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-th/strings.xml b/core/res/res/values-mcc334-mnc020-th/strings.xml
new file mode 100644
index 0000000..e4e62f0
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-th/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"การตรวจสอบสิทธิ์ล้มเหลว -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"ไม่ได้สมัครใช้บริการ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"ไม่อนุญาตการเชื่อมต่อ PDN หลายรายการสำหรับ APN ที่กำหนด -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-tl/strings.xml b/core/res/res/values-mcc334-mnc020-tl/strings.xml
new file mode 100644
index 0000000..e05ef9b
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-tl/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"HINDI NA-AUTHENTICATE -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"HINDI NAKA-SUBSCRIBE SA SERBISYO -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Hindi pinapayagan ang maraming PDN na koneksyon para sa isang partikular na APN -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-tr/strings.xml b/core/res/res/values-mcc334-mnc020-tr/strings.xml
new file mode 100644
index 0000000..7c83aee
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-tr/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"KİMLİK DOĞRULAMA HATASI -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"HİZMETE ABONE OLUNMADI -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Belirli bir APN için birden fazla PDN bağlantısına izin verilmez -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-uk/strings.xml b/core/res/res/values-mcc334-mnc020-uk/strings.xml
new file mode 100644
index 0000000..dc18a3e
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-uk/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"ПОМИЛКА АВТЕНТИФІКАЦІЇ -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"НЕМАЄ ПІДПИСКИ НА СЕРВІС -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"З цієї APN не можна підключатися до кількох відкритих мереж -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-ur/strings.xml b/core/res/res/values-mcc334-mnc020-ur/strings.xml
new file mode 100644
index 0000000..25b074e
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-ur/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <!-- no translation found for config_pdp_reject_user_authentication_failed (4683454131283459978) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_service_not_subscribed (9021140729932308119) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_multi_conn_to_same_pdn_not_allowed (3838388706348367865) -->
+ <skip />
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-uz/strings.xml b/core/res/res/values-mcc334-mnc020-uz/strings.xml
new file mode 100644
index 0000000..321aff2
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-uz/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"HAQIQIYLIK TEKSHIRUVIDA XATO -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"XIZMATGA OBUNA QILINMAGAN -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"APN uchun bir nechta PDN ulanishiga ruxsat berilmagan -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-vi/strings.xml b/core/res/res/values-mcc334-mnc020-vi/strings.xml
new file mode 100644
index 0000000..bb77bb52
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-vi/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"KHÔNG XÁC THỰC ĐƯỢC -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"CHƯA ĐĂNG KÝ DỊCH VỤ -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Không cho phép có nhiều kết nối PDN với một APN đã cho -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-zh-rCN/strings.xml b/core/res/res/values-mcc334-mnc020-zh-rCN/strings.xml
new file mode 100644
index 0000000..25b074e
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-zh-rCN/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <!-- no translation found for config_pdp_reject_user_authentication_failed (4683454131283459978) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_service_not_subscribed (9021140729932308119) -->
+ <skip />
+ <!-- no translation found for config_pdp_reject_multi_conn_to_same_pdn_not_allowed (3838388706348367865) -->
+ <skip />
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-zh-rHK/strings.xml b/core/res/res/values-mcc334-mnc020-zh-rHK/strings.xml
new file mode 100644
index 0000000..af20578
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-zh-rHK/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"認證錯誤 -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"未訂閱服務 -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"指定的 APN 不允許有多個 PDN 連線 -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-zh-rTW/strings.xml b/core/res/res/values-mcc334-mnc020-zh-rTW/strings.xml
new file mode 100644
index 0000000..2d74d5a
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-zh-rTW/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"驗證失敗 -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"未訂閱服務 -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"指定的 APN 不允許有多個 PDN 連線 -55-"</string>
+</resources>
diff --git a/core/res/res/values-mcc334-mnc020-zu/strings.xml b/core/res/res/values-mcc334-mnc020-zu/strings.xml
new file mode 100644
index 0000000..ef0f3cb
--- /dev/null
+++ b/core/res/res/values-mcc334-mnc020-zu/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+** 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="config_pdp_reject_dialog_title" msgid="41208110171880430"></string>
+ <string name="config_pdp_reject_user_authentication_failed" msgid="4683454131283459978">"UKWEHLULEKA KOKUFAKAZELA UBUQINISO -29-"</string>
+ <string name="config_pdp_reject_service_not_subscribed" msgid="9021140729932308119">"AWUBHALISELE ISEVISI -33-"</string>
+ <string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="3838388706348367865">"Uxhumano lwe-PDN oluningi lwe-APN enikeziwe aluvunyelwe -55-"</string>
+</resources>
diff --git a/core/res/res/values-mk-television/strings.xml b/core/res/res/values-mk-television/strings.xml
new file mode 100644
index 0000000..89e17a9
--- /dev/null
+++ b/core/res/res/values-mk-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Микрофонот е блокиран"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Камерата е блокирана"</string>
+</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 0b01a09..396097d 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"упатува и управува со телефонски повици"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Телесни сензори"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"пристапува до податоците од сензорите за виталните знаци"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Известувања"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"да прикажува известувања"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Преземе содржина на прозорец"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Ја следи содржината на прозорецот со кој се комуницира."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Вклучи „Истражувај со допир“"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Овозможува апликацијата да го оневозможи заклучувањето и каква било безбедност поврзана со лозинка. На пример, телефонот го оневозможува заклучувањето при прием на телефонски повик, а потоа повторно го овозможува заклучувањето кога повикот ќе заврши."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"побарува сложеност за заклучувањето на екранот"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Дозволува апликацијата да го научи нивото на сложеност за заклучувањето на екранот (високо, средно, ниско или нема), коешто ги означува можниот опсег на должината и типот на заклучувањето на екранот. Апликацијата може да им дава предлози на корисниците да го ажурираат заклучувањето на екранот на одредено ниво, но корисниците можат да го игнорираат и да продолжат понатаму. Имајте предвид дека заклучувањето на екранот не се складира како обичен текст, па апликацијата не ја знае точната лозинка."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"да прикажува известувања"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Ѝ дозволува на апликацијата да прикажува известувања"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"користи биометриски хардвер"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Дозволува апликацијата да користи биометриски хардвер за проверка"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"управувај хардвер за отпечатоци"</string>
diff --git a/core/res/res/values-ml-television/strings.xml b/core/res/res/values-ml-television/strings.xml
new file mode 100644
index 0000000..ee11b86
--- /dev/null
+++ b/core/res/res/values-ml-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"മൈക്രോഫോൺ ബ്ലോക്ക് ചെയ്തിരിക്കുന്നു"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"ക്യാമറ ബ്ലോക്ക് ചെയ്തിരിക്കുന്നു"</string>
+</resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 5301a88..b63447a 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -326,6 +326,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ഫോൺ വിളിക്കുകയും നിയന്ത്രിക്കുകയും ചെയ്യുക"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"ബോഡി സെൻസർ"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"നിങ്ങളുടെ ജീവാധാര ലക്ഷണങ്ങളെ കുറിച്ചുള്ള സെൻസർ വിവരങ്ങൾ ആക്സസ് ചെയ്യുക"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"വിൻഡോ ഉള്ളടക്കം വീണ്ടെടുക്കുക"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"നിങ്ങൾ സംവദിക്കുന്ന ഒരു വിൻഡോയുടെ ഉള്ളടക്കം പരിശോധിക്കുക."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"സ്പർശനം വഴി പര്യവേക്ഷണം ചെയ്യുക, ഓണാക്കുക"</string>
@@ -549,6 +553,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"കീലോക്കും ഏതെങ്കിലും അനുബന്ധ പാസ്വേഡ് സുരക്ഷയും പ്രവർത്തനരഹിതമാക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഉദാഹരണത്തിന്, ഒരു ഇൻകമിംഗ് കോൾ സ്വീകരിക്കുമ്പോൾ ഫോൺ കീലോക്ക് പ്രവർത്തനരഹിതമാക്കുന്നു, കോൾ അവസാനിക്കുമ്പോൾ കീലോക്ക് വീണ്ടും പ്രവർത്തനക്ഷമമാകുന്നു."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"സ്ക്രീൻ ലോക്ക് സങ്കീർണ്ണത അഭ്യർത്ഥിക്കുക"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"സ്ക്രീൻ ലോക്കിന്റെ സാധ്യമായ നീളവും തരവും സൂചിപ്പിക്കുന്ന, അതിന്റെ സങ്കീർണ്ണത നില (ഉയർന്നത്, ഇടത്തരം, കുറഞ്ഞത് അല്ലെങ്കിൽ ഒന്നുമില്ല) മനസ്സിലാക്കാൻ ആപ്പിനെ അനുവദിക്കുന്നു. സ്ക്രീൻ ലോക്ക് ഒരു പ്രത്യേക തലത്തിലേക്ക് അപ്ഡേറ്റ് ചെയ്യാൻ ഉപയോക്താക്കളെ നിർദ്ദേശിക്കാനും ആപ്പിനാവും, പക്ഷെ ഉപയോക്താക്കൾക്ക് എളുപ്പത്തിൽ അവഗണിക്കാനും മറ്റൊന്നിലേക്ക് നാവിഗേറ്റ് ചെയ്യാനുമാവും. പ്ലെയിൻടെക്സ്റ്റിൽ സ്ക്രീൻ ലോക്ക് സംഭരിക്കപ്പെട്ടിട്ടില്ലെന്ന കാര്യം ശ്രദ്ധിക്കുക, അതിനാൽ ആപ്പിന് കൃത്യമായ പാസ്വേഡ് അറിയില്ല."</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"ബയോമെട്രിക് ഹാർഡ്വെയർ ഉപയോഗിക്കുക"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"പരിശോധിച്ചുറപ്പിക്കുന്നതിനായി, ബയോമെട്രിക് ഹാർഡ്വെയർ ഉപയോഗിക്കാൻ ആപ്പിനെ അനുവദിക്കുക"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"ഫിംഗർപ്രിന്റ് ഹാർഡ്വെയർ നിയന്ത്രിക്കുക"</string>
diff --git a/core/res/res/values-mn-television/strings.xml b/core/res/res/values-mn-television/strings.xml
new file mode 100644
index 0000000..ad22bde
--- /dev/null
+++ b/core/res/res/values-mn-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Микрофоныг блоклосон байна"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Камерыг блоклосон байна"</string>
+</resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 7140487..f479b73 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"утасны дуудлага хийх, дуудлага удирдах"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Биеийн мэдрэгч"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"таны биеийн байдлын талаарх мэдрэгч бүхий өгөгдөлд нэвтрэх"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Мэдэгдэл"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"мэдэгдэл харуулах"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Цонхны агуулгыг авах"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Таны харилцан үйлчлэх цонхны контентоос шалгах."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Хүрэлтээр сонсохыг асаах"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Апп нь түгжээ болон бусад холбоотой нууц үгийн аюулгүй байдлыг идэвхгүй болгох боломжтой. Жишээ нь бол утас нь дуудлага ирэх үед түгжээг идэвхгүй болгох ба дуудлага дуусахад буцаан идэвхтэй болгодог."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"дэлгэцийн түгжээний төвөгтэй байдлын хүсэлт тавих"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Аппад дэлгэцийн түгжээний боломжтой уртын хэмжээ болон төрлийг заадаг дэлгэцийн түгжээний төвөгтэй байдлын түвшнийг (өндөр, дундаж, бага эсвэл байхгүй) мэдэж авахыг зөвшөөрдөг. Түүнчлэн, апп хэрэглэгчдэд дэлгэцийн түгжээг тодорхой түвшинд шинэчлэхийг санал болгох боломжтой хэдий ч хэрэглэгч үүнийг чөлөөтэй үл хэрэгсэж, орхих боломжтой. Дэлгэцийн түгжээг ил бичвэрээр хадгалдаггүй тул энэ апп тодорхой нууц үгийг мэддэггүй болохыг анхаарна уу."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"мэдэгдэл харуулах"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Аппад мэдэгдэл харуулахыг зөвшөөрнө"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"биометрийн техник хангамжийг ашиглах"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Aппад биометрийн техник хангамжийг баталгаажуулалтад ашиглахыг зөвшөөрдөг"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"хурууны хээний төхөөрөмжийг удирдах"</string>
diff --git a/core/res/res/values-mr-television/strings.xml b/core/res/res/values-mr-television/strings.xml
new file mode 100644
index 0000000..e0b13be
--- /dev/null
+++ b/core/res/res/values-mr-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"मायक्रोफोन ब्लॉक केला आहे"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"कॅमेरा ब्लॉक केला आहे"</string>
+</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index b42a310..4da28dc 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -326,6 +326,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"फोन कॉल आणि व्यवस्थापित"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"शरीर सेन्सर"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"आपल्या महत्त्वाच्या मापनांविषयी सेन्सर डेटा अॅक्सेस करा"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"विंडोमधील आशय पुन्हा मिळवा"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"तुम्ही वापरत असलेल्या विंडोमधील आशय तपासा."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"स्पर्श करून अन्वेषण सुरू करा"</string>
@@ -549,6 +553,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"कीलॉक आणि कोणतीही संबद्ध पासवर्ड सुरक्षितता अक्षम करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, येणारा फोन कॉल प्राप्त करताना फोन कीलॉक अक्षम करतो, नंतर जेव्हा कॉल समाप्त होतो तेव्हा तो कीलॉक पुन्हा-सक्षम करतो."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"स्क्रीन लॉक क्लिष्टतेची विनंती करा"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"अॅपला स्क्रीन लॉक क्लिष्टता पातळी (उच्च, मध्यम, खालची किंवा काहीही नाही) जाणून घेऊ देते, जी लांबीची संभाव्य रेंज आणि स्क्रीन लॉकचा प्रकार सूचित करते. अॅप वापरकर्त्यांना असेदेखील सुचवू शकते की त्यांनी स्क्रीन लॉक ठराविक पातळीपर्यंत अपडेट करावे, परंतु वापरकर्ते त्याकडे मोकळेपणाने दुर्लक्ष करू शकतात आणि तेथून नेव्हिगेट करू शकतात. स्क्रीन लॉक प्लेनटेक्स्टमध्ये स्टोअर केले जात नसल्यामुळे अॅपला नेमका पासवर्ड माहीत नसतो याची नोंद घ्या."</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"बायोमेट्रिक हार्डवेअर वापरा"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"ऑथेंटिकेशनसाठी बायोमेट्रिक हार्डवेअरचा वापर करण्याची ॲपला अनुमती देते"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"फिंगरप्रिंट हार्डवेअर व्यवस्थापित करा"</string>
@@ -1040,7 +1048,7 @@
<string name="menu_space_shortcut_label" msgid="5949311515646872071">"स्पेस"</string>
<string name="menu_enter_shortcut_label" msgid="6709499510082897320">"एंटर"</string>
<string name="menu_delete_shortcut_label" msgid="4365787714477739080">"हटवा"</string>
- <string name="search_go" msgid="2141477624421347086">"Search"</string>
+ <string name="search_go" msgid="2141477624421347086">"शोध"</string>
<string name="search_hint" msgid="455364685740251925">"शोधा…"</string>
<string name="searchview_description_search" msgid="1045552007537359343">"शोधा"</string>
<string name="searchview_description_query" msgid="7430242366971716338">"शोध क्वेरी"</string>
diff --git a/core/res/res/values-ms-television/strings.xml b/core/res/res/values-ms-television/strings.xml
new file mode 100644
index 0000000..e937401
--- /dev/null
+++ b/core/res/res/values-ms-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofon disekat"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera disekat"</string>
+</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 4ecac17..6021f70 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"membuat dan mengurus panggilan telefon"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Penderia tubuh"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"akses data penderia tentang tanda vital anda"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Pemberitahuan"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"tunjukkan pemberitahuan"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Dapatkan kembali kandungan tetingkap"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Periksa kandungan tetingkap yang berinteraksi dengan anda."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Hidupkan Teroka melalui Sentuhan"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Membenarkan apl melumpuhkan kunci kekunci dan sebarang keselamatan kata laluan yang berkaitan. Sebagai contoh, telefon melumpuhkan kunci kekunci apabila menerima panggilan telefon masuk kemudian mendayakan semula kunci kekunci apabila panggilan selesai."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"minta kerumitan kunci skrin"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Benarkan apl mengetahui tahap kekompleksan kunci skrin (tinggi, sederhana, rendah atau tiada) yang menunjukkan kemungkinan julat panjang dan jenis kunci skrin. Apl juga boleh mencadangkan kepada pengguna supaya mengemas kini kunci skrin pada tahap tertentu namun pengguna boleh mengabaikan dan menavigasi keluar dengan bebas. Sila ambil perhatian bahawa kunci skrin tidak disimpan dalam format teks biasa, maka apl tidak mengetahui kata laluan yang tepat."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"tunjukkan pemberitahuan"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Membenarkan apl menunjukkan pemberitahuan"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"gunakan perkakasan biometrik"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Membenarkan apl menggunakan perkakasan biometrik untuk pengesahan"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"urus perkakasan cap jari"</string>
diff --git a/core/res/res/values-my-television/strings.xml b/core/res/res/values-my-television/strings.xml
new file mode 100644
index 0000000..12e522d
--- /dev/null
+++ b/core/res/res/values-my-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"မိုက်ခရိုဖုန်းကို ပိတ်ထားသည်"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"ကင်မရာကို ပိတ်ထားသည်"</string>
+</resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index f0216d6..8f415ec 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ဖုန်းခေါ်ဆိုမှုများ ပြုလုပ်ရန်နှင့် စီမံရန်"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"စက်၏ အာရုံခံစနစ်များ"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"သင်၏အရေးပြီးသော ကျန်းမာရေးလက္ခဏာဆိုင်ရာ အာရုံခံကိရိယာဒေတာကို ရယူရန်"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"အကြောင်းကြားချက်များ"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"အကြောင်းကြားချက်များ ပြနိုင်သည်"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ဝင်းဒိုးတွင် ပါရှိသည်များကို ပြန်လည်ရယူရန်"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"သင်အသုံးပြုနေသော ဝင်းဒိုးတွင် ပါရှိသည်များကို ကြည့်ရှုစစ်ဆေးသည်။"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"တို့ထိခြင်းဖြင့် ရှာဖွေမှုကို ဖွင့်ရန်"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"အပလီကေးရှင်းအား သော့ချခြင်းနှင့် သက်ဆိုင်ရာ စကားဝှက်သတ်မှတ်ခြင်းများအား မသုံးနိုင်အောင် ပိတ်ခြင်းကို ခွင့်ပြုရန်။ ဥပမာ ဖုန်းလာလျှင် သော့ပိတ်ခြင်း ပယ်ဖျက်ခြင်း၊ ဖုန်းပြောပြီးလျှင် သော့ကို အလိုအလျောက် ပြန်ပိတ်ခြင်း"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"ဖုန်းမျက်နှာပြင် လော့ခ်ချရန် ရှုပ်ထွေးမှုအဆင့် တောင်းခံခြင်း"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"ဖုန်းမျက်နှာပြင်လော့ခ်၏ ရှုပ်ထွေးမှုအဆင့် (မြင့်၊ အလယ်အလတ်၊ နိမ့် သို့မဟုတ် မရှိ) အား လေ့လာရန် အက်ပ်ကို ခွင့်ပြုသည်။ ၎င်းက သတ်မှတ်ထားနိုင်သော ဖုန်းမျက်နှာပြင်လော့ခ်၏ စာလုံးရေနှင့် အမျိုးအစားကို ညွှန်ပြပေးသည်။ အသုံးပြုသူများအနေနှင့် ဖုန်းမျက်နှာပြင်လော့ခ်ကို အတိုင်းအတာတစ်ခုအထိ အဆင့်မြှင့်ရန် အက်ပ်က အကြံပြုနိုင်သည်။ သို့သော်လည်း အသုံးပြုသူများက ၎င်းကို ဂရုပြုမနေဘဲ လွတ်လပ်စွာ ကြည့်ရှုနိုင်ပါသည်။ ဖုန်းမျက်နှာပြင်လော့ခ်ကို စာသားအတိုင်း သိမ်းမထားသဖြင့် အက်ပ်သည် စကားဝှက်အစစ်ကို မသိနိုင်ကြောင်း သတိပြုပါ။"</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"အကြောင်းကြားချက်များ ပြခြင်း"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"အကြောင်းကြားချက်များ ပြရန် အက်ပ်ကို ခွင့်ပြုနိုင်သည်"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"ဇီဝဗေဒဆိုင်ရာ အချက်အလက်သုံး ကွန်ပျူတာဆိုင်ရာ စက်ပစ္စည်းကို အသုံးပြုရန်"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"အထောက်အထားစိစစ်ခြင်းအတွက် ဇီဝဗေဒဆိုင်ရာ သတင်းအချက်အလက်များသုံးသည့် ကွန်ပျူတာဆိုင်ရာ စက်ပစ္စည်းကို အသုံးပြုရန် အက်ပ်ကို ခွင့်ပြုသည်"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"လက်ဗွေစစ်ပစ္စည်းကို စီမံခြင်း"</string>
diff --git a/core/res/res/values-nb-television/strings.xml b/core/res/res/values-nb-television/strings.xml
new file mode 100644
index 0000000..0b41516
--- /dev/null
+++ b/core/res/res/values-nb-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofonen er blokkert"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kameraet er blokkert"</string>
+</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 0e23cd4..ec4378f 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ring og administrer anrop"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Kroppssensorer"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"få tilgang til sensordata om de vitale tegnene dine"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Varsler"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"vise varsler"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"hente innhold i vinduer"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Appen analyserer innholdet i vinduer du samhandler med."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"slå på berøringsutforsking"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Lar appen deaktivere tastelåsen og eventuell tilknyttet passordsikkerhet. Et eksempel er at telefonen deaktiverer tastelåsen når du mottar et innkommende anrop, og deretter aktiverer tastelåsen igjen når samtalen er ferdig."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"be om skjermlåsens kompleksitet"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Lar appen lære skjermlåsens kompleksitetsnivå (høy, middels, lav eller ingen), som indikerer det mulige området for lengde og type skjermlås. Appen kan foreslå at brukeren oppdaterer skjermlåsen til et bestemt nivå, men brukere kan velge å ignorere dette og navigere bort. Vær oppmerksom på at skjermlåsen ikke er lagret klartekst, så appen kan ikke se det nøyaktige passordet."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"vise varsler"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Lar appen vise varsler"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"bruke biometrisk maskinvare"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Lar appen bruke biometrisk maskinvare til godkjenning"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"administrere fingeravtrykkmaskinvare"</string>
diff --git a/core/res/res/values-ne-television/strings.xml b/core/res/res/values-ne-television/strings.xml
new file mode 100644
index 0000000..32dfdc43
--- /dev/null
+++ b/core/res/res/values-ne-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"माइक्रोफोन ब्लक गरिएको छ"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"क्यामेरा ब्लक गरिएको छ"</string>
+</resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index ae69fcc..9d74bb0 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"फोन कलहरू गर्नुहोस् र व्यवस्थापन गर्नुहोस्"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"शारीरिक सेन्सरहरू"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"तपाईंको महत्त्वपूर्ण संकेत बारे सेन्सर डेटा पहुँच गर्नुहोस्"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"सूचनाहरू"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"सूचनाहरू देखाइयोस्"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"विन्डो सामग्रीको पुनःबहाली गर्नुहोस्।"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"तपाईँको अन्तरक्रिया भइरहेको विन्डोको सामग्रीको निरीक्षण गर्नुहोस्।"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"छोएर गरिने खोजलाई सुचारु गर्नुहोस्"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"कुनै सम्बन्धित पासवर्ड सुरक्षा र किलकलाई असक्षम पार्न एपलाई अनुमति दिन्छ। उदाहरणको लागि, अन्तर्गमन फोन कल प्राप्त गर्दा फोनले किलकलाई असक्षम पार्छ, त्यसपछि कल सकिएको बेला किलक पुनःसक्षम पार्छ।"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"स्क्रिन लकको जटिलतासम्बन्धी जानकारी प्राप्त गर्ने अनुरोध गर्नुहोस्"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"यसले एपलाई स्क्रिन लकको जटिलताको स्तर (उच्च, मध्यम, न्यून वा कुनै पनि होइन) थाहा पाउने अनुमति दिन्छ जसले स्क्रिन लकको लम्बाइको सम्भावित दायरा र त्यसको प्रकारलाई जनाउँछ। यसै गरी, यो एपले प्रयोगकर्ताहरूलाई स्क्रिन लक अद्यावधिक गर्ने सुझाव पनि दिन सक्छ तर प्रयोगकर्ताहरू उक्त सुझावको बेवास्ता गरी बाहिर निस्कन सक्छन्। स्क्रिन लक सादा पाठको ढाँचामा भण्डारण नगरिने हुँदा यो एपलाई वास्तविक पासवर्ड थाहा नहुने कुराको हेक्का राख्नुहोस्।"</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"सूचनाहरू देखाइयोस्"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"यो एपलाई सूचना देखाउन दिनुहोस्"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"बायोमेट्रिक हार्डवेयर प्रयोग गर्नुहोस्"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"एपलाई प्रमाणीकरणका लागि बायोमेट्रिक हार्डवेयर प्रयोग गर्न अनुमति दिन्छ"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"फिंगरप्रिन्ट हार्डवेयर व्यवस्थापन गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl-television/strings.xml b/core/res/res/values-nl-television/strings.xml
new file mode 100644
index 0000000..41a95b3
--- /dev/null
+++ b/core/res/res/values-nl-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Microfoon is geblokkeerd"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Camera is geblokkeerd"</string>
+</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 5377be8..06ef43e 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"telefoneren en gesprekken beheren"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Lichaamssensoren"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"toegang krijgen tot sensorgegevens over je vitale functies"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Meldingen"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"meldingen laten zien"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Content van vensters ophalen"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"De content inspecteren van een venster waarmee je interactie hebt."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Verkennen via aanraking aanzetten"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Hiermee kan de app de toetsenblokkering en bijbehorende wachtwoordbeveiliging uitzetten. Zo kan de telefoon de toetsenblokkering uitzetten als je wordt gebeld en de toetsenblokkering weer aanzetten als het gesprek is beëindigd."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"complexiteit van schermvergrendeling opvragen"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Hiermee krijgt de app toestemming om het complexiteitsniveau van de schermvergrendeling te achterhalen (hoog, midden, laag of geen). Dat geeft een indicatie van het mogelijke lengtebereik en type van de schermvergrendeling. De app kan gebruikers ook voorstellen de schermvergrendeling naar een bepaald niveau te updaten, maar gebruikers kunnen dit altijd negeren en de app verlaten. De schermvergrendeling wordt niet opgeslagen als platte tekst, zodat de app het precieze wachtwoord niet weet."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"meldingen laten zien"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Hiermee kan de app meldingen laten zien"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"biometrische hardware gebruiken"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Hiermee kan de app biometrische hardware gebruiken voor verificatie"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"Vingerafdrukhardware beheren"</string>
diff --git a/core/res/res/values-or-television/strings.xml b/core/res/res/values-or-television/strings.xml
new file mode 100644
index 0000000..38c3dee
--- /dev/null
+++ b/core/res/res/values-or-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"ମାଇକ୍ରୋଫୋନକୁ ବ୍ଲକ କରାଯାଇଛି"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"କ୍ୟାମେରାକୁ ବ୍ଲକ କରାଯାଇଛି"</string>
+</resources>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 8c40899..e5e78ee 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -326,6 +326,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ଫୋନ୍ କଲ୍ କରେ ଏବଂ ପରିଚାଳନା କରେ"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"ବଡି ସେନ୍ସର୍"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"ଆପଣଙ୍କ ଗୁରୁତପୂର୍ଣ୍ଣ ସଂକେତଗୁଡ଼ିକ ବିଷୟରେ ସେନ୍ସର୍ ଡାଟା ଆକ୍ସେସ୍ କରେ"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ୱିଣ୍ଡୋ କଣ୍ଟେଣ୍ଟ ହାସଲ କରନ୍ତୁ"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ଆପଣ କାମ କରୁଥିବା ୱିଣ୍ଡୋର କଣ୍ଟେଣ୍ଟକୁ ଯାଞ୍ଚ କରନ୍ତୁ।"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"ସ୍ପର୍ଶ ଦ୍ୱାରା ଏକ୍ସପ୍ଲୋର୍ ଅନ୍ କରନ୍ତୁ"</string>
@@ -549,6 +553,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"ଆପ୍କୁ କୀ\'ଲକ୍ କିମ୍ବା ସେଥିରେ ଥିବା କୌଣସି ପାସ୍ୱର୍ଡ ସୁରକ୍ଷାକୁ ଅକ୍ଷମ କରିବା ପାଇଁ ଅନୁମତି ଦିଏ, ଉଦାହରଣସ୍ୱରୂପ, ଇନ୍କମିଙ୍ଗ ଫୋନ୍ କଲ୍ ପ୍ରାପ୍ତ କରିବା ସମୟରେ ଫୋନ୍ଟି କୀ\'ଲକ୍କୁ ଅକ୍ଷମ କରିଦିଏ, ତା’ପରେ କଲ୍ ସମାପ୍ତ ହେବାପରେ ପୁଣି କୀ\'ଲକ୍କୁ ସକ୍ଷମ କରିଥାଏ।"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"ସ୍କ୍ରିନ୍ ଲକ୍ ଜଟିଳତା ସଂକ୍ରାନ୍ତ ଅନୁରୋଧ"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"ସ୍କ୍ରିନ୍ ଲକ୍ର ଜଟିଳତା ସ୍ତର (ଉଚ୍ଚ, ମଧ୍ୟମ, ନିମ୍ନ କିମ୍ବା କିଛିନୁହେଁ), ଜାଣିବାକୁ ଆପ୍କୁ ଅନୁମତି ଦିଅନ୍ତୁ, ଯାହା ସ୍କ୍ରିନ୍ ଲକ୍ର ସମ୍ଭାବ୍ୟ ପରିସୀମାର ଲମ୍ବ ଏବଂ ପ୍ରକାର ସୂଚୀତ କରେ। ଆପ୍ ଏହା ମଧ୍ୟ ଉପଯୋଗକର୍ତ୍ତାମାନଙ୍କୁ ପରାମର୍ଶ ଦେଇପାରେ ଯେ ସେମାନେ ସ୍କ୍ରିନ୍ ଲକ୍କୁ ଏକ ନିର୍ଦ୍ଧିଷ୍ଟ ସ୍ତର ପର୍ଯ୍ୟନ୍ତ ଅପ୍ଡେଟ୍ କରିପାରନ୍ତି, କିନ୍ତୁ ଉପଯୋଗକର୍ତ୍ତାମାନେ ନିଜ ଇଚ୍ଛାରେ ଏହାକୁ ଉପେକ୍ଷା ଏବଂ ନାଭିଗେଟ୍ କରିପାରିବେ। ଧ୍ୟାନ ଦିଅନ୍ତୁ ଯେ, ସ୍କ୍ରିନ୍ ଲକ୍ ସରଳ ଟେକ୍ସଟ୍ରେ ଷ୍ଟୋର୍ କରାଯାଇନଥାଏ ତେଣୁ ଆପ୍ ସଠିକ୍ ପାସ୍ୱର୍ଡ ଜାଣିପାରି ନଥାଏ।"</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"ବାୟୋମେଟ୍ରିକ୍ ହାର୍ଡୱେର୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"ସ୍ୱୀକୃତି ପାଇଁ ବାୟୋମେଟ୍ରିକ୍ ହାର୍ଡୱେର୍ ବ୍ୟବହାର କରିବାକୁ ଅନୁମତି ଦିଏ"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"ଟିପଚିହ୍ନ ହାର୍ଡୱେର୍ ପରିଚାଳନା କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa-television/strings.xml b/core/res/res/values-pa-television/strings.xml
new file mode 100644
index 0000000..5c8e2cf
--- /dev/null
+++ b/core/res/res/values-pa-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਬਲਾਕ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"ਕੈਮਰਾ ਬਲਾਕ ਕੀਤਾ ਗਿਆ ਹੈ"</string>
+</resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 383ef18..4cfcd80 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ਫ਼ੋਨ ਕਾਲਾਂ ਕਰਨ ਅਤੇ ਉਹਨਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"ਸਰੀਰ ਸੰਬੰਧੀ ਸੈਂਸਰ"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"ਆਪਣੇ ਸਰੀਰ ਦੇ ਅਹਿਮ ਚਿੰਨ੍ਹਾਂ ਬਾਰੇ ਸੰਵੇਦਕ ਡਾਟਾ ਤੱਕ ਪਹੁੰਚ ਕਰਨ"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"ਸੂਚਨਾਵਾਂ"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"ਸੂਚਨਾਵਾਂ ਦਿਖਾਓ"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ਵਿੰਡੋ ਸਮੱਗਰੀ ਮੁੜ ਪ੍ਰਾਪਤ ਕਰਨਾ"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ਉਸ ਵਿੰਡੋ ਸਮੱਗਰੀ ਦੀ ਜਾਂਚ ਕਰੋ, ਜਿਸ ਨਾਲ ਤੁਸੀਂ ਅੰਤਰਕਿਰਿਆ ਕਰ ਰਹੇ ਹੋ"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"\'ਸਪੱਰਸ਼ ਰਾਹੀਂ ਪੜਚੋਲ ਕਰੋ\' ਚਾਲੂ ਕਰਨਾ"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"ਐਪ ਨੂੰ ਕੀਲਾਕ ਅਤੇ ਕਿਸੇ ਵੀ ਸੰਬੰਧਿਤ ਪਾਸਵਰਡ ਸੁਰੱਖਿਆ ਨੂੰ ਬੰਦ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਉਦਾਹਰਨ ਲਈ, ਫ਼ੋਨ ਇੱਕ ਇਨਕਮਿੰਗ ਫ਼ੋਨ ਕਾਲ ਪ੍ਰਾਪਤ ਕਰਨ ਵੇਲੇ ਬੰਦ ਕਰਦਾ ਹੈ, ਫਿਰ ਜਦੋਂ ਕਾਲ ਖਤਮ ਹੁੰਦੀ ਹੈ ਤਾਂ ਕੀਲਾਕ ਨੂੰ ਮੁੜ-ਚਾਲੂ ਕਰਦਾ ਹੈ।"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਜਟਿਲਤਾ ਲਈ ਬੇਨਤੀ ਕਰੋ"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"ਐਪ ਨੂੰ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਜਟਿਲਤਾ ਦੇ ਪੱਧਰ ਬਾਰੇ ਜਾਣਨ ਦਿਓ (ਉੱਚ, ਮੱਧਮ, ਘੱਟ ਜਾਂ ਕੋਈ ਨਹੀਂ), ਜੋ ਲੰਬਾਈ ਦੀ ਸੰਭਵ ਰੇਂਜ ਅਤੇ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਕਿਸਮ ਦਾ ਸੰਕੇਤ ਦਿੰਦਾ ਹੈ। ਐਪ ਵਰਤੋਂਕਾਰਾਂ ਨੂੰ ਇੱਕ ਖਾਸ ਪੱਧਰ ਤੱਕ ਸਕ੍ਰੀਨ ਲਾਕ ਅੱਪਡੇਟ ਕਰਨ ਲਈ ਸੁਝਾਅ ਵੀ ਦੇ ਸਕਦੀ ਹੈ ਪਰ ਵਰਤੋਂਕਾਰ ਇਸਨੂੰ ਅਣਡਿੱਠ ਕਰ ਸਕਦੇ ਹਨ। ਨੋਟ ਕਰੋ ਕਿ ਸਕ੍ਰੀਨ ਲਾਕ ਸਧਾਰਨ ਲਿਖਤ ਵਿੱਚ ਸਟੋਰ ਨਹੀਂ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਇਸ ਲਈ ਐਪ ਸਹੀ ਪਾਸਵਰਡ ਬਾਰੇ ਨਹੀਂ ਜਾਣਦੀ ਹੈ।"</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"ਸੂਚਨਾਵਾਂ ਦਿਖਾਓ"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"ਇਸ ਨਾਲ ਐਪ ਨੂੰ ਸੂਚਨਾਵਾਂ ਦਿਖਾਉਣ ਦੀ ਆਗਿਆ ਮਿਲਦੀ ਹੈ"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"ਬਾਇਓਮੈਟ੍ਰਿਕ ਹਾਰਡਵੇਅਰ ਵਰਤੋ"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"ਐਪ ਨੂੰ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਬਾਇਓਮੈਟ੍ਰਿਕ ਹਾਰਡਵੇਅਰ ਵਰਤਣ ਦਿੰਦਾ ਹੈ"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਵਿਵਸਥਿਤ ਕਰੋ"</string>
@@ -2011,7 +2015,7 @@
<string name="app_category_image" msgid="7307840291864213007">"ਫ਼ੋਟੋਆਂ ਅਤੇ ਚਿੱਤਰ"</string>
<string name="app_category_social" msgid="2278269325488344054">"ਸਮਾਜਕ ਅਤੇ ਸੰਚਾਰ"</string>
<string name="app_category_news" msgid="1172762719574964544">"ਖਬਰਾਂ ਅਤੇ ਰਸਾਲੇ"</string>
- <string name="app_category_maps" msgid="6395725487922533156">"ਨਕਸ਼ੇ ਅਤੇ ਆਵਾਗੌਣ"</string>
+ <string name="app_category_maps" msgid="6395725487922533156">"Maps ਅਤੇ ਨੈਵੀਗੇਸ਼ਨ"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ਉਤਪਾਦਕਤਾ"</string>
<string name="app_category_accessibility" msgid="6643521607848547683">"ਪਹੁੰਚਯੋਗਤਾ"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"ਡੀਵਾਈਸ ਸਟੋਰੇਜ"</string>
diff --git a/core/res/res/values-pl-television/strings.xml b/core/res/res/values-pl-television/strings.xml
new file mode 100644
index 0000000..4c51034
--- /dev/null
+++ b/core/res/res/values-pl-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofon jest zablokowany"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Aparat jest zablokowany"</string>
+</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index cbd8900..176dffe 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -332,6 +332,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"nawiązywanie połączeń telefonicznych i zarządzanie nimi"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Czujniki na ciele"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"dostęp do danych czujnika podstawowych funkcji życiowych"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Powiadomienia"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"pokazuj powiadomienia"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Pobieranie zawartości okna"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Sprawdzanie zawartości okna, z którego korzystasz."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Włączenie czytania dotykiem"</string>
@@ -555,6 +557,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Pozwala aplikacji na wyłączenie blokady klawiatury i wszystkich związanych z tym haseł zabezpieczających. Na przykład telefon wyłącza blokadę klawiatury, gdy odbiera połączenie przychodzące, a następnie włącza ją ponownie po zakończeniu połączenia."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"żądaj informacji o stopniu złożoności blokady ekranu"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Zezwala aplikacji na poznanie stopnia złożoności blokady ekranu (wysoki, średni, niski lub brak), który wskazuje na możliwy zakres długości oraz typ blokady ekranu. Aplikacja może też zasugerować zaktualizowanie blokady ekranu pod kątem określonego stopnia trudności, ale użytkownik może to zignorować i zamknąć komunikat. Pamiętaj, że blokada ekranu nie jest zapisywana jako tekst jawny, więc aplikacja nie pozna dokładnego hasła."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"pokazuj powiadomienia"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Zezwala aplikacji na pokazywanie powiadomień"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"używanie sprzętu biometrycznego"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Zezwala aplikacji na używanie sprzętu biometrycznego na potrzeby autoryzacji"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"zarządzanie czytnikiem linii papilarnych"</string>
diff --git a/core/res/res/values-pt-rBR-television/strings.xml b/core/res/res/values-pt-rBR-television/strings.xml
new file mode 100644
index 0000000..af4df828
--- /dev/null
+++ b/core/res/res/values-pt-rBR-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"O microfone está bloqueado"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"A câmara está bloqueada"</string>
+</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index eb250cf..a4eb8f6 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"faça e gerencie chamadas telefônicas"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensores corporais"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"acesse dados do sensor sobre seus sinais vitais"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificações"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"mostrar notificações"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Acessar conteúdo de uma janela"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspeciona o conteúdo de uma janela com a qual você está interagindo."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Ativar Explorar por toque"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Permite que o app desative o bloqueio de teclas e qualquer segurança por senha associada. Por exemplo, o telefone desativa o bloqueio de telas ao receber uma chamada e o reativa quando a chamada é finalizada."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"Solicitar complexidade do bloqueio de tela"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permite que o app saiba o nível de complexidade do bloqueio de tela (alto, médio, baixo ou nenhum), que indica o intervalo possível de comprimento e o tipo de bloqueio de tela. O app também pode sugerir a atualização do bloqueio de tela até um certo nível, mas os usuários podem ignorar a sugestão. O bloqueio de tela não é armazenado em texto simples, então o app não tem acesso à senha exata."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"mostrar notificações"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Permite que o app mostre notificações"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"Usar hardware de biometria"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Permite que o app use hardware de biometria para autenticação"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"gerenciar hardware de impressão digital"</string>
diff --git a/core/res/res/values-pt-rPT-television/strings.xml b/core/res/res/values-pt-rPT-television/strings.xml
new file mode 100644
index 0000000..af4df828
--- /dev/null
+++ b/core/res/res/values-pt-rPT-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"O microfone está bloqueado"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"A câmara está bloqueada"</string>
+</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index cd4adb0..7e9520d 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"fazer e gerir chamadas"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensores de corpo"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"aceder a dados do sensor acerca dos seus sinais vitais"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificações"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"mostrar notificações"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Obter conteúdo da janela"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspecionar o conteúdo de uma janela com a qual está a interagir."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Ativar Explorar Através do Toque"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Permite que a app desative o bloqueio de teclas e qualquer segurança por palavra-passe associada. Por exemplo, o telemóvel desativa o bloqueio de teclas quando recebe uma chamada e reativa o bloqueio de teclas ao terminar a chamada."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"solicitar a complexidade do bloqueio de ecrã"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permite que a app aprenda o nível de complexidade do bloqueio de ecrã (elevado, médio, baixo ou nenhum), que indica o intervalo de comprimento e o tipo de bloqueio de ecrã possíveis. A app também pode sugerir aos utilizadores que atualizem o bloqueio de ecrã para um determinado nível, mas estes podem ignorar livremente a sugestão e continuar a navegação. Tenha em atenção que o bloqueio de ecrã não é armazenado em texto simples, pelo que a app desconhece a palavra-passe exata."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"mostrar notificações"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Permite à app mostrar notificações"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"Utilizar hardware biométrico"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Permite que a app utilize hardware biométrico para autenticação."</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"gerir o hardware de impressão digital"</string>
diff --git a/core/res/res/values-pt-television/strings.xml b/core/res/res/values-pt-television/strings.xml
new file mode 100644
index 0000000..af4df828
--- /dev/null
+++ b/core/res/res/values-pt-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"O microfone está bloqueado"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"A câmara está bloqueada"</string>
+</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index eb250cf..a4eb8f6 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"faça e gerencie chamadas telefônicas"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensores corporais"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"acesse dados do sensor sobre seus sinais vitais"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificações"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"mostrar notificações"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Acessar conteúdo de uma janela"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspeciona o conteúdo de uma janela com a qual você está interagindo."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Ativar Explorar por toque"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Permite que o app desative o bloqueio de teclas e qualquer segurança por senha associada. Por exemplo, o telefone desativa o bloqueio de telas ao receber uma chamada e o reativa quando a chamada é finalizada."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"Solicitar complexidade do bloqueio de tela"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permite que o app saiba o nível de complexidade do bloqueio de tela (alto, médio, baixo ou nenhum), que indica o intervalo possível de comprimento e o tipo de bloqueio de tela. O app também pode sugerir a atualização do bloqueio de tela até um certo nível, mas os usuários podem ignorar a sugestão. O bloqueio de tela não é armazenado em texto simples, então o app não tem acesso à senha exata."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"mostrar notificações"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Permite que o app mostre notificações"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"Usar hardware de biometria"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Permite que o app use hardware de biometria para autenticação"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"gerenciar hardware de impressão digital"</string>
diff --git a/core/res/res/values-ro-television/strings.xml b/core/res/res/values-ro-television/strings.xml
new file mode 100644
index 0000000..9291dde
--- /dev/null
+++ b/core/res/res/values-ro-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Microfonul este blocat"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Camera este blocată"</string>
+</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 41ecdbd..adb5618 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -329,6 +329,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"inițieze și să gestioneze apeluri telefonice"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Senzori corporali"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"acceseze datele de la senzori despre semnele vitale"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificări"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"să afișeze notificări"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Analizeze conținutul ferestrei"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspectează conținutul unei ferestre cu care interacționați."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activeze funcția Explorați prin atingere"</string>
@@ -552,6 +554,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Permite aplicației să dezactiveze blocarea tastelor și orice modalitate asociată de securizare prin parolă. De exemplu, telefonul dezactivează blocarea tastelor când se primește un apel telefonic și reactivează blocarea tastelor la terminarea apelului."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"solicitați complexitatea blocării ecranului"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Permite aplicației să învețe nivelul de complexitate al blocării ecranului (ridicat, mediu, scăzut sau fără), fapt ce indică intervalul posibil de lungime a parolei și tipul de blocare a ecranului. Aplicația le poate sugera utilizatorilor să își actualizeze blocarea ecranului la un anumit nivel, dar utilizatorii pot ignora sugestia și pot naviga în continuare. Rețineți că blocarea ecranului nu este stocată ca text simplu, astfel încât aplicația să nu cunoască parola exactă."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"să afișeze notificări"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Permite aplicației să afișeze notificări"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"utilizați hardware biometric"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Permite aplicației să folosească hardware biometric pentru autentificare"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"gestionează hardware-ul pentru amprentă"</string>
diff --git a/core/res/res/values-ru-television/strings.xml b/core/res/res/values-ru-television/strings.xml
new file mode 100644
index 0000000..16571da
--- /dev/null
+++ b/core/res/res/values-ru-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Микрофон заблокирован"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Камера заблокирована"</string>
+</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index ef8561d..acdc7db 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -332,6 +332,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"осуществлять вызовы и управлять ими"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Нательные датчики"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"доступ к данным датчиков о состоянии организма"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Уведомления"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"показ уведомлений"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Получать содержимое окна"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Анализировать содержимое активного окна."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Включать Изучение касанием"</string>
@@ -555,6 +557,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Приложение сможет отключать блокировку экрана и другие функции защиты. Например, блокировка экрана будет отключаться при получении входящего вызова и включаться после завершения разговора."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"Запрос данных об уровне сложности блокировки экрана"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Приложение получит доступ к сведениям об уровне сложности блокировки экрана (высокий, средний, низкий или отсутствует), в том числе о типе блокировки и длине пароля. Кроме того, оно сможет предлагать пользователям повысить уровень сложности блокировки. Эти рекомендации необязательны. Обратите внимание, что пароль не хранится в виде открытого текста и недоступен приложению."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"показ уведомлений"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Приложение сможет показывать уведомления."</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"Использование биометрического оборудования"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Приложение сможет использовать биометрическое оборудование для аутентификации"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"управление сканером отпечатков пальцев"</string>
diff --git a/core/res/res/values-si-television/strings.xml b/core/res/res/values-si-television/strings.xml
new file mode 100644
index 0000000..71c7bde
--- /dev/null
+++ b/core/res/res/values-si-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"මයික්රෆෝනය අවහිර කර ඇත"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"කැමරාව අවහිර කර ඇත"</string>
+</resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index af59be3..f565433 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"දුරකථන ඇමතුම් සිදු කිරීම සහ කළමනාකරණය කිරීම"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"ශරීර සංවේදක"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"ඔබේ ජෛව ලක්ෂණ පිළිබඳ සංවේදක දත්ත වෙත පිවිසෙන්න"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"දැනුම්දීම්"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"දැනුම්දීම් පෙන්වන්න"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"කවුළු අන්න්තර්ගතය ලබාගන්න"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ඔබ අන්තර්ක්රියාකාරී වන කවුළුවේ අන්තර්ගතය පරීක්ෂා කරන්න."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"ස්පර්ශයෙන් ගවේෂණය සක්රිය කරන්න"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"යතුරු අගුල සහ ඕනෑම සම්බන්ධිත මුරපද ආරක්ෂාවක් අබල කිරීමට යෙදුමට අවසර දෙන්න. මෙහි උදාහරණයක් වන්නේ පැමිණෙන ඇමතුමක් ලැබෙද්දී, දුරකථනය අක්රිය වන අතර ඇමතුම අවසාන වන විට යතුරු අගුල නැවත සක්රිය වෙයි."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"තිර අඟුලු සංකීර්ණතාව ඉල්ලන්න"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"යෙදුමට තිර අගුලෙහි තිබිය හැකි දිගෙහි පරාසය සහ වර්ගය පිළිබිඹු කරන, තිර අඟුලු සංකීර්ණතා මට්ටම (ඉහළ, මධ්යම, අඩු හෝ රහිත) දැන ගැනීමට ඉඩ දෙයි. යෙදුම පරිශීලකයින්ට තිර අඟුල නිශ්චිත මට්ටමකට යාවත්කාලීන කිරීමට යෝජනා කළ හැකි නමුත් ඔවුන්ට නිදහසේ නොසලකා හැර ඉවතට සංචලන කළ හැක. තිර අඟුල සරල පෙළින් ගබඩා කර නැති බැවින් යෙදුම නිවැරදි මුරපදය නොදන්නා බව සලකන්න."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"දැනුම්දීම් පෙන්වන්න"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"දැනුම්දීම් පෙන්වීමට යෙදුමට ඉඩ දෙයි"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"ජීවමිතික දෘඪාංග භාවිත කරන්න"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"සත්යාපනය සඳහා ජීවමිතික දෘඪාංග භාවිත කිරීමට යෙදුමට ඉඩ දෙයි"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"ඇඟිලි සලකුණු දෘඩාංග කළමනාකරණය කිරීම."</string>
diff --git a/core/res/res/values-sk-television/strings.xml b/core/res/res/values-sk-television/strings.xml
new file mode 100644
index 0000000..37a7668
--- /dev/null
+++ b/core/res/res/values-sk-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofón je blokovaný"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera je blokovaná"</string>
+</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 7c56c49..aa23ee3 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -332,6 +332,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"telefonovanie a správu hovorov"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Telové senzory"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"prístup k dátam senzorov vašich životných funkcií"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Upozornenia"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"zobrazovať upozornenia"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Načítať obsah okna"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Môžete preskúmať obsah okna, s ktorým pracujete."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Zapnúť funkciu Preskúmanie dotykom"</string>
@@ -555,6 +557,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Umožňuje aplikácii zakázať uzamknutie klávesnice a akékoľvek súvisiace zabezpečenie heslom. Príkladom je zakázanie uzamknutia klávesnice pri prichádzajúcom telefonickom hovore a jeho opätovné povolenie po skončení hovoru."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"požadovať zložitosť zámky obrazovky"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Umožňuje aplikácii zapamätať si úroveň zložitosti zámky obrazovky (vysoká, stredná, nízka alebo žiadna), ktorá udáva pravdepodobný rozsah dĺžky a typu zámky obrazovky. Aplikácia tiež navrhuje používateľom aktualizáciu zámky obrazovky na určitú úroveň, používatelia sa však môžu na základe vlastného uváženia rozhodnúť tento návrh ignorovať a prejsť inam. Upozorňujeme, že zámka obrazovky nie je uložená vo forme obyčajného textu, takže aplikácia nepozná presné heslo."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"zobrazovať upozornenia"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Povolí aplikácii zobrazovať upozornenia"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"používať biometrický hardvér"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Umožňuje aplikácii používať na overenie totožnosti biometrický hardvér"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"spravovať hardvér na snímanie odtlačkov prstov"</string>
diff --git a/core/res/res/values-sl-television/strings.xml b/core/res/res/values-sl-television/strings.xml
new file mode 100644
index 0000000..fed14b7
--- /dev/null
+++ b/core/res/res/values-sl-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofon je blokiran"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Fotoaparat je blokiran"</string>
+</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index deab3a4..4426146 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -332,6 +332,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"opravljanje in upravljanje telefonskih klicev"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Tipala telesnih funkcij"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"dostop do podatkov tipala o vaših vitalnih znakih"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Pridobiti vsebino okna"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Preverjanje vsebine okna, ki ga uporabljate."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Vklopiti raziskovanje z dotikom"</string>
@@ -555,6 +559,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Aplikaciji dovoljuje, da onemogoči zaklep tipkovnice in morebitno povezano varnostno geslo. Telefon na primer onemogoči zaklep tipkovnice pri dohodnem klicu ter vnovič omogoči zaklep, ko je klic končan."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"zahteva zapletenost zaklepanja zaslona"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Aplikaciji dovoljuje, da pridobi raven zapletenosti zaklepanja zaslona (visoka, srednja, nizka ali brez), ki označuje možen obseg dolžine in vrsto zaklepanja zaslona. Aplikacija lahko tudi predlaga uporabnikom, da posodobijo zaklepanje zaslona na določeno raven, vendar lahko uporabniki to opozorilo prezrejo in ga zaprejo tako, da se pomaknejo stran. Upoštevajte, da zaklepanje zaslona ni shranjeno v golem besedilu, tako da aplikacija ne pozna točnega gesla."</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"uporaba strojne opreme za biometrične podatke"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Aplikaciji omogoča uporabo strojne opreme za biometrične podatke za preverjanje pristnosti"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"upravljanje strojne opreme za prstne odtise"</string>
diff --git a/core/res/res/values-sq-television/strings.xml b/core/res/res/values-sq-television/strings.xml
new file mode 100644
index 0000000..6154cef
--- /dev/null
+++ b/core/res/res/values-sq-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofoni është i bllokuar"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera është e bllokuar"</string>
+</resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index c04a888..379aaf2 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"kryej dhe menaxho telefonata"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Sensorët e trupit"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"qasu tek të dhënat e sensorëve rreth shenjave të tua jetësore"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Njoftimet"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"shfaq njoftimet"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Nxjerrë përmbajtjen e dritares"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspekton përmbajtjen e dritares me të cilën po ndërvepron."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Aktivizojë funksionin \"Eksploro me prekje\""</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Lejon aplikacionin të çaktivizojë kyçjen e tasteve dhe çdo mbrojtje të lidhur me fjalëkalimin. Për shembull, telefoni çaktivizon kyçjen e tasteve kur merr një telefonatë hyrëse, pastaj riaktivizon kyçjen e tasteve kur mbaron telefonata."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"kërko kompleksitetin e kyçjes së ekranit"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Lejo që aplikacioni të mësojë nivelin e kompleksitetit të kyçjes së ekranit (i lartë, i mesëm, i ulët ose asnjë), që tregon gamën e mundshme të gjatësisë dhe llojin e kyçjes së ekranit. Aplikacioni mund t\'u sugjerojë gjithashtu përdoruesve që të përditësojnë kyçjen e ekranit në një nivel të caktuar, por përdoruesit mund ta shpërfillin lirshëm dhe të navigojnë më tej. Ki parasysh se kyçja e ekranit nuk ruhet në tekst të thjeshtë, prandaj aplikacioni nuk e di fjalëkalimin e saktë."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"shfaq njoftimet"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Lejon që aplikacioni të shfaqë njoftime"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"përdor harduerin biometrik"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"E lejon aplikacionin që të përdorë harduerin biometrik për vërtetimin"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"të menaxhojë harduerin e gjurmës së gishtit"</string>
diff --git a/core/res/res/values-sr-television/strings.xml b/core/res/res/values-sr-television/strings.xml
new file mode 100644
index 0000000..8643593
--- /dev/null
+++ b/core/res/res/values-sr-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Микрофон је блокиран"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Камера је блокирана"</string>
+</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 8d111d5..823eae9 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -329,6 +329,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"упућује телефонске позиве и управља њима"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Сензори за тело"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"приступа подацима сензора о виталним функцијама"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Обавештења"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"приказивање обавештења"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"да преузима садржај прозора"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Проверава садржај прозора са којим остварујете интеракцију."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"да укључи Истраживања додиром"</string>
@@ -552,6 +554,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Дозвољава апликацији да онемогући закључавање тастатуре и све повезане безбедносне мере са лозинкама. На пример, телефон онемогућава закључавање тастатуре при пријему долазног телефонског позива, а затим га поново омогућава по завршетку позива."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"тражење сложености закључавања екрана"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Дозвољава апликацији да сазна ниво сложености закључавања екрана (висока, средња, ниска или ниједна), што указује на могући опсег трајања и тип закључавања екрана. Апликација може и да предлаже корисницима да ажурирају закључавање екрана на одређени ниво, али корисници слободно могу да занемаре то и да иду на друге странице. Имајте на уму да се подаци за закључавање екрана не чувају као обичан текст, па апликација не зна тачну лозинку."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"приказивање обавештења"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Дозвољава апликацији да приказује обавештења"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"користи биометријски хардвер"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Дозвољава апликацији да користи биометријски хардвер за потврду идентитета"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"управљај хардвером за отиске прстију"</string>
diff --git a/core/res/res/values-sv-television/strings.xml b/core/res/res/values-sv-television/strings.xml
new file mode 100644
index 0000000..de11894d
--- /dev/null
+++ b/core/res/res/values-sv-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofonen är blockerad"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kameran är blockerad"</string>
+</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index f6cc4cf..2785f90 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ringa och hantera telefonsamtal"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Kroppssensorer"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"få åtkomst till sensordata om dina vitalparametrar"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Aviseringar"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"visa aviseringar"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Hämta fönsterinnehåll"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Granska innehållet i ett fönster som du interagerar med."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Aktivera Explore by touch"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Tillåter att appen inaktiverar tangentlåset och tillhörande lösenordsskydd. Ett exempel kan vara att tangentlåset inaktiveras vid inkommande samtal och aktiveras igen när samtalet är avslutat."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"begär komplexitetsnivå för skärmlåset"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Tillåter att appen får reda på komplexitetsnivån för skärmlåset (hög, mellan, låg eller ingen). Detta ger en indikation på skärmlåsets möjliga längd och typ. Appen kan även föreslå att skärmlåset uppdateras till en viss nivå, men användare kan ignorera förslaget och fortsätta navigera. Observera att skärmlåset inte lagras i vanlig text så appen får inte reda på det exakta lösenordet."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"visa aviseringar"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Tillåter att appen visar aviseringar"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"använd biometrisk maskinvara"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Tillåter att appen använder biometrisk maskinvara vid autentisering"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"hantera maskinvara för fingeravtryck"</string>
diff --git a/core/res/res/values-sw-television/strings.xml b/core/res/res/values-sw-television/strings.xml
new file mode 100644
index 0000000..a61101c
--- /dev/null
+++ b/core/res/res/values-sw-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Maikrofoni imezuiwa"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera imezuiwa"</string>
+</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 8a05fdc..72774ab 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"piga na udhibiti simu"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Vitambua shughuli za mwili"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"fikia data ya kitambuzi kuhusu alama zako muhimu"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Arifa"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"kuonyesha arifa"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Kufikia maudhui ya dirisha"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Kuchunguza maudhui ya dirisha unalotumia."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Kuwasha \'Chunguza kwa Kugusa\'"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Inaruhusu programu kulemaza ufunguo wa vitufe na usalama mwingine ambata wa nenosiri. Kwa mfano, simu inalemaza ufunguo wa viitufe inapopokea simu inayoingia, kisha inawezesha upya ufunguo wa vitufe wakati simu inapokamilika."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"iombe kiwango cha uchangamano wa kufunga skrini"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Huruhusu programu kupata maelezo kuhusu kiwango cha uchangamano wa kufunga skrini (juu, wastani, chini au hakuna), ambacho huashiria urefu unaowezekana na aina ya kufunga skrini. Programu pia inaweza kumpendekezea mtumiaji asasishe mbinu ya kufunga skrini iwe ya kiwango fulani lakini mtumiaji anaweza kuamua kupuuza na kuendelea. Kumbuka kuwa maelezo ya kufunga skrini hayahifadhiwi kama maandishi, hivyo programu haitambui nenosiri mahususi."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"kuonyesha arifa"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Huruhusu programu kuonyesha arifa"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"tumia maunzi ya kibiolojia"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Huruhusu programu itumie maunzi ya kibiolojia katika uthibitishaji"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"dhibiti maunzi ya alama ya kidole"</string>
diff --git a/core/res/res/values-ta-television/strings.xml b/core/res/res/values-ta-television/strings.xml
new file mode 100644
index 0000000..4ab9935
--- /dev/null
+++ b/core/res/res/values-ta-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"மைக்ரோஃபோன் முடக்கப்பட்டுள்ளது"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"கேமரா முடக்கப்பட்டுள்ளது"</string>
+</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 5e81c60..2b5ea2e 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"யாரையும் தொலைபேசியில் அழைக்கலாம்"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"உடல் சென்சார்கள்"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"உங்கள் உடல் இயக்கம் பற்றி உணர்விகள் கூறும் தகவலைப் பார்க்கலாம்"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"அறிவிப்புகள்"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"அறிவிப்புகளைக் காட்டும்"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"சாளர உள்ளடக்கத்தைப் பெறும்"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"நீங்கள் பணியாற்றிக் கொண்டிருக்கும் சாளரத்தின் உள்ளடக்கத்தைப் பார்க்கலாம்."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"தொடுவதன் மூலம் அறிவதை இயக்கும்"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"விசைப்பூட்டையும், தொடர்புடைய கடவுச்சொல் பாதுகாப்பையும் முடக்கப் ஆப்ஸை அனுமதிக்கிறது. எடுத்துக்காட்டாக, உள்வரும் மொபைல் அழைப்பைப் பெறும்போது மொபைல் விசைப்பூட்டை முடக்குகிறது, பிறகு அழைப்பு முடிந்தவுடன் விசைப்பூட்டை மீண்டும் இயக்குகிறது."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"திரைப் பூட்டு தொடர்பான சிக்கலைத் தீர்க்க அனுமதி கோருதல்"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"திரைப் பூட்டு தொடர்பான சிக்கலின் தன்மையை (அதிகம், நடுத்தரம், குறைவு அல்லது ஏதுமில்லை) அறிந்துகொள்ள ஆப்ஸை அனுமதிக்கிறது. இதன் மூலம் திரைப் பூட்டின் அளவு வரம்பையும் வகையையும் தெரிந்துகொள்ளலாம். மேலும் திரைப் பூட்டைக் குறிப்பிட்ட நிலைக்கு மாற்றிக் கொள்ளலாம் என்பதையும் பயனர்களுக்கு ஆப்ஸ் பரிந்துரைக்க முடியும். ஆனால் தங்கள் விருப்பப்படி அவற்றைப் பயனர்கள் நிராகரிக்கவோ ஏற்கவோ இயலும். கவனத்திற்கு: திரைப் பூட்டு எளிய உரையிலானதாக சேமிக்கப்படுவதில்லை என்பதால் சரியான கடவுச்சொல்லை ஆப்ஸால் அறிய இயலாது."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"அறிவிப்புகளைக் காட்டுதல்"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"அறிவிப்புகளைக் காட்ட ஆப்ஸை அனுமதிக்கும்"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"பயோமெட்ரிக் வன்பொருளைப் பயன்படுத்து"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"பயோமெட்ரிக் வன்பொருளைப் பயன்படுத்தி அங்கீகரிப்பதற்கு, ஆப்ஸை அனுமதிக்கும்"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"கைரேகை வன்பொருளை நிர்வகி"</string>
diff --git a/core/res/res/values-te-television/strings.xml b/core/res/res/values-te-television/strings.xml
new file mode 100644
index 0000000..f2b35fc
--- /dev/null
+++ b/core/res/res/values-te-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"మైక్రోఫోన్ బ్లాక్ చేయబడింది"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"కెమెరా బ్లాక్ చేయబడింది"</string>
+</resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 4ba4c6f..0f6d47e 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -124,7 +124,7 @@
<string name="roamingTextSearching" msgid="5323235489657753486">"సేవ కోసం శోధిస్తోంది"</string>
<string name="wfcRegErrorTitle" msgid="3193072971584858020">"Wi‑Fi కాలింగ్ని సెటప్ చేయడం సాధ్యపడలేదు"</string>
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="468830943567116703">"Wi-Fiతో కాల్స్ను చేయడానికి మరియు మెసేజ్లను పంపించడానికి, మొదట ఈ సేవను సెటప్ చేయాల్సిందిగా మీ క్యారియర్కి చెప్పండి. ఆ తర్వాత సెట్టింగ్ల నుండి Wi-Fi కాలింగ్ని మళ్లీ ఆన్ చేయండి. (లోపం కోడ్: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+ <item msgid="468830943567116703">"Wi-Fiతో కాల్స్ను చేయడానికి, మెసేజ్లను పంపించడానికి, ముందుగా ఈ సర్వీస్ను సెటప్ చేయాల్సిందిగా మీ క్యారియర్ను అడగండి. ఆ తర్వాత సెట్టింగ్ల నుండి Wi-Fi కాలింగ్ను మళ్లీ ఆన్ చేయండి. (ఎర్రర్ కోడ్: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="4795145070505729156">"మీ క్యారియర్తో Wi‑Fi కాలింగ్ని నమోదు చేయడంలో సమస్య: <xliff:g id="CODE">%1$s</xliff:g>"</item>
@@ -306,10 +306,10 @@
<string name="permgroupdesc_contacts" msgid="9163927941244182567">"మీ కాంటాక్ట్లను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"లొకేషన్"</string>
<string name="permgroupdesc_location" msgid="1995955142118450685">"ఈ పరికర లొకేషన్ను యాక్సెస్ చేయడానికి"</string>
- <string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string>
+ <string name="permgrouplab_calendar" msgid="6426860926123033230">"క్యాలెండర్"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"మీ క్యాలెండర్ను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
- <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS మెసేజ్లను పంపడం మరియు వీక్షించడం"</string>
+ <string name="permgroupdesc_sms" msgid="5726462398070064542">"SMS మెసేజ్లను పంపడం, వీక్షించడం"</string>
<string name="permgrouplab_storage" msgid="1938416135375282333">"ఫైల్స్, మీడియా"</string>
<string name="permgroupdesc_storage" msgid="6351503740613026600">"మీ పరికరంలోని ఫోటోలు, మీడియా మరియు ఫైళ్లను యాక్సెస్ చేయడానికి"</string>
<string name="permgrouplab_microphone" msgid="2480597427667420076">"మైక్రోఫోన్"</string>
@@ -326,6 +326,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"ఫోన్ కాల్స్ చేయడం మరియు నిర్వహించడం"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"శరీర సెన్సార్లు"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"మీ అత్యంత కీలకమైన గుర్తుల గురించి సెన్సార్ డేటాని యాక్సెస్ చేస్తుంది"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"విండో కంటెంట్ను తిరిగి పొందుతుంది"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"మీరు పరస్పర చర్య చేస్తున్న విండో కంటెంట్ను పరిశీలిస్తుంది."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"తాకడం ద్వారా విశ్లేషణను ప్రారంభిస్తుంది"</string>
@@ -356,10 +360,10 @@
<string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"కాల్ను వేరే నంబర్కు దారి మళ్లించే లేదా మొత్తంగా కాల్ను ఆపివేసే ఎంపిక సహాయంతో అవుట్గోయింగ్ కాల్ సమయంలో డయల్ చేయబడుతున్న నంబర్ను చూడటానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"ఫోన్ కాల్స్కు సమాధానమివ్వు"</string>
<string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"ఇన్కమింగ్ ఫోన్ కాల్స్కు సమాధానమివ్వడానికి యాప్ను అనుమతిస్తుంది."</string>
- <string name="permlab_receiveSms" msgid="505961632050451881">"వచన మెసేజ్లను (SMS) స్వీకరించడం"</string>
- <string name="permdesc_receiveSms" msgid="1797345626687832285">"SMS మెసేజ్లను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. యాప్ మీ డివైజ్కు పంపబడిన మెసేజ్లను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
- <string name="permlab_receiveMms" msgid="4000650116674380275">"వచన మెసేజ్లను (MMS) స్వీకరించడం"</string>
- <string name="permdesc_receiveMms" msgid="958102423732219710">"MMS మెసేజ్లను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. యాప్ మీ డివైజ్కు పంపబడిన మెసేజ్లను మీకు చూపకుండానే పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
+ <string name="permlab_receiveSms" msgid="505961632050451881">"టెక్స్ట్ మెసేజ్లను (SMS) స్వీకరించడం"</string>
+ <string name="permdesc_receiveSms" msgid="1797345626687832285">"SMS మెసేజ్లను స్వీకరించడానికి, ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. మీ డివైజ్కు వచ్చిన మెసేజ్లను మీకు చూపకుండానే యాప్ పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
+ <string name="permlab_receiveMms" msgid="4000650116674380275">"టెక్స్ట్ మెసేజ్లను (MMS) స్వీకరించడం"</string>
+ <string name="permdesc_receiveMms" msgid="958102423732219710">"MMS మెసేజ్లను స్వీకరించడానికి, ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. మీ డివైజ్కు వచ్చిన మెసేజ్లను మీకు చూపకుండానే యాప్ పర్యవేక్షించగలదని లేదా తొలగించగలదని దీని అర్థం."</string>
<string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"సెల్ ప్రసార మెసేజ్లను ఫార్వర్డ్ చేయడం"</string>
<string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"సెల్ ప్రసార మెసేజ్లను స్వీకరించినప్పుడు, వాటిని ఫార్వర్డ్ చేయడానికి సెల్ ప్రసార మాడ్యూల్కు కట్టుబడి ఉండేందుకు యాప్ను అనుమతిస్తుంది. ఎమర్జెన్సీ పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని లొకేషన్లలో సెల్ ప్రసార అలర్ట్లు డెలివరీ చేయబడతాయి. ఎమర్జెన్సీ సెల్ ప్రసార అలర్ట్ను స్వీకరించినప్పుడు హానికరమైన యాప్లు మీ పరికరం పనితీరుకు లేదా నిర్వహణకు ఆటంకం కలిగించే అవకాశం ఉంది."</string>
<string name="permlab_manageOngoingCalls" msgid="281244770664231782">"కొనసాగుతున్న కాల్స్ను మేనేజ్ చేయి"</string>
@@ -368,14 +372,14 @@
<string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"మీ పరికరం స్వీకరించిన సెల్ ప్రసార మెసేజ్లను చదవడానికి యాప్ను అనుమతిస్తుంది. ఎమర్జెన్సీ పరిస్థితుల గురించి మిమ్మల్ని హెచ్చరించడానికి కొన్ని లొకేషన్లలో సెల్ ప్రసార అలర్ట్లు డెలివరీ చేయబడతాయి. ఎమర్జెన్సీ సెల్ ప్రసార అలర్ట్ను స్వీకరించినప్పుడు హానికరమైన యాప్లు మీ పరికరం పనితీరుకు లేదా నిర్వహణకు ఆటంకం కలిగించే అవకాశం ఉంది."</string>
<string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"చందా చేయబడిన ఫీడ్లను చదవడం"</string>
<string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"ప్రస్తుతం సమకాలీకరించిన ఫీడ్ల గురించి వివరాలను పొందడానికి యాప్ను అనుమతిస్తుంది."</string>
- <string name="permlab_sendSms" msgid="7757368721742014252">"SMS మెసేజ్లను పంపడం మరియు వీక్షించడం"</string>
+ <string name="permlab_sendSms" msgid="7757368721742014252">"SMS మెసేజ్లను పంపడం, వీక్షించడం"</string>
<string name="permdesc_sendSms" msgid="6757089798435130769">"SMS మెసేజ్లు పంపడానికి యాప్ను అనుమతిస్తుంది. దీని వలన ఊహించని ఛార్జీలు విధించబడవచ్చు. హానికరమైన యాప్లు మీ నిర్ధారణ లేకుండానే మెసేజ్లను పంపడం ద్వారా మీకు డబ్బు ఖర్చయ్యేలా చేయవచ్చు."</string>
- <string name="permlab_readSms" msgid="5164176626258800297">"మీ వచన మెసేజ్లు (SMS లేదా MMS) చదవడం"</string>
- <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"ఈ యాప్ మీ టాబ్లెట్లో నిల్వ చేసిన అన్ని SMS (వచన) మెసేజ్లను చదవగలదు."</string>
- <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"ఈ యాప్ మీ Android TV పరికరంలో నిల్వ అయిన SMS (వచనం) సందేశాలన్నింటినీ చదవగలదు."</string>
- <string name="permdesc_readSms" product="default" msgid="774753371111699782">"ఈ యాప్ మీ ఫోన్లో నిల్వ చేసిన అన్ని SMS (వచన) మెసేజ్లను చదవగలదు."</string>
- <string name="permlab_receiveWapPush" msgid="4223747702856929056">"వచన మెసేజ్లను (WAP) స్వీకరించడం"</string>
- <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP మెసేజ్లను స్వీకరించడానికి మరియు ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఈ అనుమతి మీకు పంపబడిన మెసేజ్లను మీకు చూపకుండానే పర్యవేక్షించగల లేదా తొలగించగల సామర్థ్యాన్ని కలిగి ఉంటుంది."</string>
+ <string name="permlab_readSms" msgid="5164176626258800297">"మీ టెక్స్ట్ మెసేజ్లు (SMS లేదా MMS) చదవడం"</string>
+ <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"ఈ యాప్ మీ టాబ్లెట్లో స్టోర్ చేసిన అన్ని SMS (టెక్స్ట్) మెసేజ్లను చదవగలదు."</string>
+ <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"ఈ యాప్ మీ Android TV పరికరంలో స్టోర్ అయిన SMS (టెక్స్ట్) మెసేజ్లు అన్నింటిని చదవగలదు."</string>
+ <string name="permdesc_readSms" product="default" msgid="774753371111699782">"ఈ యాప్ మీ ఫోన్లో నిల్వ చేసిన అన్ని SMS (టెక్స్ట్) మెసేజ్లను చదవగలదు."</string>
+ <string name="permlab_receiveWapPush" msgid="4223747702856929056">"టెక్స్ట్ మెసేజ్లను (WAP) స్వీకరించడం"</string>
+ <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP మెసేజ్లను స్వీకరించడానికి, ప్రాసెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఈ అనుమతి మీకు వచ్చిన మెసేజ్లను మీకు చూపకుండానే పర్యవేక్షించగల లేదా తొలగించగల సామర్థ్యాన్ని కలిగి ఉంటుంది."</string>
<string name="permlab_getTasks" msgid="7460048811831750262">"అమలవుతున్న యాప్లను పునరుద్ధరించడం"</string>
<string name="permdesc_getTasks" msgid="7388138607018233726">"ప్రస్తుతం మరియు ఇటీవల అమలవుతున్న విధుల గురించి వివరణాత్మక సమాచారాన్ని తిరిగి పొందడానికి యాప్ను అనుమతిస్తుంది. ఇది పరికరంలో ఉపయోగించబడిన యాప్ల గురించి సమాచారాన్ని కనుగొనడానికి యాప్ను అనుమతించవచ్చు."</string>
<string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"ప్రొఫైల్ మరియు పరికర యజమానులను నిర్వహించడం"</string>
@@ -431,9 +435,9 @@
<string name="permdesc_readCalendar" product="tv" msgid="5811726712981647628">"ఈ యాప్ మీ Android TV పరికరంలో నిల్వ చేసిన క్యాలెండర్ ఈవెంట్లన్నీ చదవగలదు, మీ క్యాలెండర్ డేటాను షేర్ చేయగలదు లేదా సేవ్ చేయగలదు."</string>
<string name="permdesc_readCalendar" product="default" msgid="9118823807655829957">"ఈ యాప్ మీ ఫోన్లో నిల్వ చేసిన క్యాలెండర్ ఈవెంట్లన్నీ చదవగలదు మరియు మీ క్యాలెండర్ డేటాను షేర్ చేయగలదు లేదా సేవ్ చేయగలదు."</string>
<string name="permlab_writeCalendar" msgid="6422137308329578076">"యజమానికి తెలియకుండానే క్యాలెండర్ ఈవెంట్లను జోడించి లేదా సవరించి, అతిథులకు ఇమెయిల్ పంపడం"</string>
- <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"ఈ యాప్ మీ టాబ్లెట్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
- <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"ఈ యాప్ మీ Android TV పరికరంలో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
- <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"ఈ యాప్ మీ ఫోన్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ యజమానుల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి యజమానులకు తెలియకుండానే మార్చగలదు."</string>
+ <string name="permdesc_writeCalendar" product="tablet" msgid="8722230940717092850">"ఈ యాప్ మీ టాబ్లెట్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ ఓనర్ల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి ఓనర్లకు తెలియకుండానే మార్చగలదు."</string>
+ <string name="permdesc_writeCalendar" product="tv" msgid="951246749004952706">"ఈ యాప్ మీ Android TV పరికరంలో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ ఓనర్ల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు లేదా ఈవెంట్లను వాటి ఓనర్లకు తెలియకుండానే మార్చగలదు."</string>
+ <string name="permdesc_writeCalendar" product="default" msgid="5416380074475634233">"ఈ యాప్ మీ ఫోన్లో క్యాలెండర్ ఈవెంట్లను జోడించగలదు, తీసివేయగలదు లేదా మార్చగలదు. ఈ యాప్ క్యాలెండర్ ఓనర్ల నుండి వచ్చినట్లుగా మెసేజ్లను పంపగలదు, లేదా ఈవెంట్లను వాటి ఓనర్లకు తెలియకుండానే మార్చగలదు."</string>
<string name="permlab_accessLocationExtraCommands" msgid="5162339812057983988">"అదనపు లొకేషన్ ప్రొవైడర్ కమాండ్లను యాక్సెస్ చేయడం"</string>
<string name="permdesc_accessLocationExtraCommands" msgid="355369611979907967">"అదనపు లొకేషన్ ప్రొవైడర్ కమాండ్లను యాక్సెస్ చేయడానికి యాప్ను అనుమతిస్తుంది. ఇది GPS లేదా ఇతర లొకేషన్ సోర్స్ల నిర్వహణలో యాప్ ప్రమేయం ఉండేలా అనుమతించవచ్చు."</string>
<string name="permlab_accessFineLocation" msgid="6426318438195622966">"స్క్రీన్పై ఉన్నప్పుడు మాత్రమే ఖచ్చితమైన లొకేషన్ను యాక్సెస్ చేయండి"</string>
@@ -549,6 +553,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"కీలాక్ మరియు ఏదైనా అనుబంధించబడిన పాస్వర్డ్ భద్రతను నిలిపివేయడానికి యాప్ను అనుమతిస్తుంది. ఉదాహరణకు, ఇన్కమింగ్ ఫోన్ కాల్ వస్తున్నప్పుడు ఫోన్ కీలాక్ను నిలిపివేస్తుంది, ఆపై కాల్ ముగిసిన తర్వాత కీలాక్ను మళ్లీ ప్రారంభిస్తుంది."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"స్క్రీన్ లాక్ సంక్లిష్టత రిక్వెస్ట్"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"ఇది మీ స్క్రీన్ లాక్ పాస్వర్డ్ సంక్లిష్టత స్థాయి (తీవ్రంగా ఉండాలా, ఓ మోస్తరుగా ఉండాలా, తక్కువ తీవ్రంగా ఉండాలా లేదా అస్సలు తీవ్రత ఉండకూడదా) తెలుసుకోవడానికి యాప్ను అనుమతిస్తుంది, అంటే పొడుగు ఎంత ఉండాలి, ఏ రకమైన స్క్రీన్ లాక్ పధ్ధతి అనుసరించాలో సూచిస్తుంది. అలాగే, స్క్రీన్ లాక్ పాస్వర్డ్ సంక్లిష్టతను ఏ స్థాయికి సెట్ చేసుకుంటే బాగుంటుందో కూడా వినియోగదారులకు యాప్ సూచించగలదు, కానీ వినియోగదారులు నిరభ్యంతరంగా ఆ సూచనలను పట్టించుకోకుండా వారి ఇష్టం మేరకు చక్కగా సెట్ చేసుకోవచ్చు. ఇంకో ముఖ్య విషయం, స్క్రీన్ లాక్ అన్నది సాదా వచన రూపంలో నిల్వ చేయబడదు, కనుక ఖచ్చితమైన పాస్వర్డ్ ఏమిటనేది యాప్కు తెలియదు."</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"బయోమెట్రిక్ హార్డ్వేర్ని ఉపయోగించు"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"ప్రమాణీకరణ కోసం బయోమెట్రిక్ హార్డ్వేర్ను ఉపయోగించడానికి యాప్ని అనుమతిస్తుంది"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"వేలిముద్ర హార్డ్వేర్ని నిర్వహించడానికి అనుమతి"</string>
@@ -817,7 +825,7 @@
<string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string>
<string name="phoneTypeWorkMobile" msgid="7522314392003565121">"కార్యాలయ మొబైల్"</string>
<string name="phoneTypeWorkPager" msgid="3748332310638505234">"కార్యాలయ పేజర్"</string>
- <string name="phoneTypeAssistant" msgid="757550783842231039">"Assistant"</string>
+ <string name="phoneTypeAssistant" msgid="757550783842231039">"అసిస్టెంట్"</string>
<string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string>
<string name="eventTypeCustom" msgid="3257367158986466481">"అనుకూలం"</string>
<string name="eventTypeBirthday" msgid="7770026752793912283">"పుట్టినరోజు"</string>
@@ -850,7 +858,7 @@
<string name="orgTypeOther" msgid="5450675258408005553">"ఇతరం"</string>
<string name="orgTypeCustom" msgid="1126322047677329218">"అనుకూలం"</string>
<string name="relationTypeCustom" msgid="282938315217441351">"అనుకూలం"</string>
- <string name="relationTypeAssistant" msgid="4057605157116589315">"Assistant"</string>
+ <string name="relationTypeAssistant" msgid="4057605157116589315">"అసిస్టెంట్"</string>
<string name="relationTypeBrother" msgid="7141662427379247820">"సోదరుడు"</string>
<string name="relationTypeChild" msgid="9076258911292693601">"బిడ్డ"</string>
<string name="relationTypeDomesticPartner" msgid="7825306887697559238">"జీవిత భాగస్వామి"</string>
@@ -1014,7 +1022,7 @@
<string name="permlab_setAlarm" msgid="1158001610254173567">"అలారం సెట్ చేయడం"</string>
<string name="permdesc_setAlarm" msgid="2185033720060109640">"ఇన్స్టాల్ చేయబడిన అలారం గడియారం యాప్లో అలారంను సెట్ చేయడానికి యాప్ను అనుమతిస్తుంది. కొన్ని అలారం గల గడియారం యాప్లు ఈ ఫీచర్ను అమలు చేయకపోవచ్చు."</string>
<string name="permlab_addVoicemail" msgid="4770245808840814471">"వాయిస్ మెయిల్ను జోడించడం"</string>
- <string name="permdesc_addVoicemail" msgid="5470312139820074324">"మీ వాయిస్ మెయిల్ ఇన్బాక్స్కి మెసేజ్లను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string>
+ <string name="permdesc_addVoicemail" msgid="5470312139820074324">"మీ వాయిస్ మెయిల్ ఇన్బాక్స్కు మెసేజ్లను జోడించడానికి యాప్ను అనుమతిస్తుంది."</string>
<string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"బ్రౌజర్ భౌగోళిక స్థానం అనుమతులను సవరించడం"</string>
<string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"బ్రౌజర్ యొక్క భౌగోళిక లొకేషన్ అనుమతులను సవరించడానికి యాప్ను అనుమతిస్తుంది. హానికరమైన యాప్లు ఏకపక్ష వెబ్ సైట్లకు లొకేషన్ సమాచారాన్ని అనుమతించడానికి దీన్ని ఉపయోగించవచ్చు."</string>
<string name="save_password_message" msgid="2146409467245462965">"మీరు బ్రౌజర్ ఈ పాస్వర్డ్ను గుర్తుపెట్టుకోవాలని కోరుకుంటున్నారా?"</string>
@@ -1043,7 +1051,7 @@
<string name="search_go" msgid="2141477624421347086">"సెర్చ్"</string>
<string name="search_hint" msgid="455364685740251925">"సెర్చ్ చేయండి..."</string>
<string name="searchview_description_search" msgid="1045552007537359343">"సెర్చ్"</string>
- <string name="searchview_description_query" msgid="7430242366971716338">"ప్రశ్నను వెతకండి"</string>
+ <string name="searchview_description_query" msgid="7430242366971716338">"సెర్చ్ క్వెరీ"</string>
<string name="searchview_description_clear" msgid="1989371719192982900">"ప్రశ్నను క్లియర్ చేయి"</string>
<string name="searchview_description_submit" msgid="6771060386117334686">"ప్రశ్నని సమర్పించండి"</string>
<string name="searchview_description_voice" msgid="42360159504884679">"వాయిస్ సెర్చ్"</string>
@@ -1413,7 +1421,7 @@
<string name="ext_media_new_notification_message" msgid="6095403121990786986">"సెటప్ చేయడానికి నొక్కండి"</string>
<string name="ext_media_new_notification_message" product="tv" msgid="216863352100263668">"సెటప్ చేయడానికి ఎంచుకోండి"</string>
<string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"మీరు పరికరాన్ని తిరిగి ఫార్మాట్ చేయాల్సి ఉంటుంది. తొలగించడానికి ట్యాప్ చేయండి"</string>
- <string name="ext_media_ready_notification_message" msgid="777258143284919261">"ఫోటోలు మరియు మీడియాను బదిలీ చేయడానికి"</string>
+ <string name="ext_media_ready_notification_message" msgid="777258143284919261">"ఫోటోలు, మీడియాను బదిలీ చేయడానికి"</string>
<string name="ext_media_ready_notification_message" product="tv" msgid="8847134811163165935">"మీడియా ఫైల్స్ను బ్రౌజ్ చేయండి"</string>
<string name="ext_media_unmountable_notification_title" msgid="4895444667278979910">"<xliff:g id="NAME">%s</xliff:g>తో సమస్య ఉంది"</string>
<string name="ext_media_unmountable_notification_title" product="automotive" msgid="3142723758949023280">"<xliff:g id="NAME">%s</xliff:g> పని చేయటం లేదు"</string>
@@ -1525,7 +1533,7 @@
<string name="progress_erasing" msgid="6891435992721028004">"షేర్ చేసిన నిల్వను తొలగిస్తోంది…"</string>
<string name="share" msgid="4157615043345227321">"షేర్"</string>
<string name="find" msgid="5015737188624767706">"కనుగొనండి"</string>
- <string name="websearch" msgid="5624340204512793290">"వెబ్ శోధన"</string>
+ <string name="websearch" msgid="5624340204512793290">"వెబ్ సెర్చ్"</string>
<string name="find_next" msgid="5341217051549648153">"తదుపరిదాన్ని కనుగొను"</string>
<string name="find_previous" msgid="4405898398141275532">"మునుపటిదాన్ని కనుగొను"</string>
<string name="gpsNotifTicker" msgid="3207361857637620780">"<xliff:g id="NAME">%s</xliff:g> నుండి లొకేషన్ రిక్వెస్ట్"</string>
@@ -2011,7 +2019,7 @@
<string name="app_category_image" msgid="7307840291864213007">"ఫోటోలు, ఇమేజ్లు"</string>
<string name="app_category_social" msgid="2278269325488344054">"సామాజికం & కమ్యూనికేషన్"</string>
<string name="app_category_news" msgid="1172762719574964544">"వార్తలు & వార్తాపత్రికలు"</string>
- <string name="app_category_maps" msgid="6395725487922533156">"Maps & నావిగేషన్"</string>
+ <string name="app_category_maps" msgid="6395725487922533156">"మ్యాప్స్ & నావిగేషన్"</string>
<string name="app_category_productivity" msgid="1844422703029557883">"ఉత్పాదకత"</string>
<string name="app_category_accessibility" msgid="6643521607848547683">"యాక్సెసిబిలిటీ"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"పరికర నిల్వ"</string>
@@ -2056,7 +2064,7 @@
<string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"ప్రశాంతంగా ఉండండి మరియు దగ్గర్లో తలదాచుకోండి."</string>
<string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"వెంటనే తీర ప్రాంతాలు మరియు నదీ పరీవాహక ప్రాంతాలను ఖాళీ చేసి మెట్ట ప్రాంతాలకు తరలి వెళ్లండి."</string>
<string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"ప్రశాంతంగా ఉండండి మరియు దగ్గర్లో తలదాచుకోండి."</string>
- <string name="etws_primary_default_message_test" msgid="4583367373909549421">"అత్యవసర మెసేజ్ల పరీక్ష"</string>
+ <string name="etws_primary_default_message_test" msgid="4583367373909549421">"అత్యవసర మెసేజ్ల టెస్ట్"</string>
<string name="notification_reply_button_accessibility" msgid="5235776156579456126">"రిప్లయి పంపండి"</string>
<string name="etws_primary_default_message_others" msgid="7958161706019130739"></string>
<string name="mmcc_authentication_reject" msgid="4891965994643876369">"వాయిస్ కోసం SIM అనుమతించబడదు"</string>
@@ -2102,7 +2110,7 @@
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"సరే"</string>
<string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"ఆఫ్ చేయండి"</string>
<string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"మరింత తెలుసుకోండి"</string>
- <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12లో Android అనుకూల నోటిఫికేషన్లను, మెరుగైన నోటిఫికేషన్లు భర్తీ చేశాయి. సూచించిన చర్యలు, రిప్లయిలను ఈ ఫీచర్ చూపించి, మీ నోటిఫికేషన్లను ఆర్గనైజ్ చేస్తుంది.\n\nకాంటాక్ట్ పేర్లు, మెసేజ్లు లాంటి వ్యక్తిగత సమాచారంతో సహా నోటిఫికేషన్ కంటెంట్ను మెరుగైన నోటిఫికేషన్లు యాక్సెస్ చేయవచ్చు. ఫోన్ కాల్స్కు సమాధానమివ్వడం, \'అంతరాయం కలిగించవద్దు\' ఆప్షన్ను కంట్రోల్ చేయడం లాంటి నోటిఫికేషన్లను విస్మరించడం లేదా ప్రతిస్పందించడం కూడా ఈ ఫీచర్ చేయగలదు."</string>
+ <string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Android 12లో Android అనుకూల నోటిఫికేషన్లను, మెరుగైన నోటిఫికేషన్లు రీప్లేస్ చేశాయి. ఈ ఫీచర్, సూచించిన చర్యలను, రిప్లయిలను చూపించి, మీ నోటిఫికేషన్లను ఆర్గనైజ్ చేస్తుంది.\n\nకాంటాక్ట్ పేర్లు, మెసేజ్లు లాంటి వ్యక్తిగత సమాచారంతో పాటు నోటిఫికేషన్ కంటెంట్ను మెరుగైన నోటిఫికేషన్లు యాక్సెస్ చేస్తాయి. ఫోన్ కాల్స్కు సమాధానమివ్వడం, \'అంతరాయం కలిగించవద్దు\' ఆప్షన్ను కంట్రోల్ చేయడం వంటి నోటిఫికేషన్లను విస్మరించడం లేదా వాటికి ప్రతిస్పందించడం కూడా ఈ ఫీచర్ చేయగలదు."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"రొటీన్ మోడ్ సమాచార నోటిఫికేషన్"</string>
<string name="dynamic_mode_notification_title" msgid="9205715501274608016">"మామూలుగా ఛార్జ్ చేసేలోపు బ్యాటరీ ఖాళీ కావచ్చు"</string>
<string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"బ్యాటరీ జీవితకాలాన్ని పెంచడానికి బ్యాటరీ సేవర్ యాక్టివేట్ చేయబడింది"</string>
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 55e5685..606d0f2 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -45,5 +45,18 @@
<!-- Component name of the activity used to inform a user about a sensory being blocked because
of privacy settings. -->
- <string name="config_sensorUseStartedActivity">com.android.systemui/com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity</string>
+ <string name="config_sensorUseStartedActivity" translatable="false">
+ com.android.systemui/com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity
+ </string>
+
+ <!-- Component name of the activity that shows the request for access to a usb device. -->
+ <string name="config_usbPermissionActivity" translatable="false">
+ com.android.systemui/com.android.systemui.usb.tv.TvUsbPermissionActivity
+ </string>
+
+ <!-- Component name of the activity that confirms the activity to start when a usb device is
+ plugged in. -->
+ <string name="config_usbConfirmActivity" translatable="false">
+ com.android.systemui/com.android.systemui.usb.tv.TvUsbConfirmActivity
+ </string>
</resources>
diff --git a/core/res/res/values-th-television/strings.xml b/core/res/res/values-th-television/strings.xml
new file mode 100644
index 0000000..13468b8
--- /dev/null
+++ b/core/res/res/values-th-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"ไมโครโฟนถูกบล็อก"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"กล้องถูกบล็อก"</string>
+</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 0056d79..986f64d 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"โทรและจัดการการโทร"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"เซ็นเซอร์ร่างกาย"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"เข้าถึงข้อมูลเซ็นเซอร์เกี่ยวกับสัญญาณชีพของคุณ"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"การแจ้งเตือน"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"แสดงการแจ้งเตือน"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"เรียกข้อมูลเนื้อหาของหน้าต่าง"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"ตรวจสอบเนื้อหาของหน้าต่างที่คุณกำลังโต้ตอบอยู่"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"เปิด \"แตะเพื่อสำรวจ\""</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"อนุญาตให้แอปพลิเคชันปิดใช้งานการล็อกปุ่มกดและการรักษาความปลอดภัยด้วยรหัสผ่านใดๆ ที่เกี่ยวข้อง ตัวอย่างเช่น โทรศัพท์ปิดใช้งานการล็อกปุ่มกดเมื่อรับสายเรียกเข้า จากนั้นจึงเปิดใช้งานการล็อกปุ่มกดใหม่หลังจากวางสาย"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"ขอความซับซ้อนของการล็อกหน้าจอ"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"อนุญาตให้แอปเรียนรู้ระดับความซับซ้อนของการล็อกหน้าจอ (สูง ปานกลาง ต่ำ หรือไม่มี) ซึ่งแสดงให้เห็นช่วงความยาวและประเภทของการล็อกหน้าจอที่เป็นไปได้ นอกจากนี้แอปยังแนะนำให้ผู้ใช้อัปเดตการล็อกหน้าจอเป็นระดับหนึ่งๆ ได้ด้วย แต่ผู้ใช้จะปฏิเสธและไปยังส่วนต่างๆ ต่อได้ โปรดทราบว่าระบบไม่ได้จัดเก็บการล็อกหน้าจอไว้เป็นข้อความธรรมดา เพื่อให้แอปไม่รู้รหัสผ่าน"</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"แสดงการแจ้งเตือน"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"อนุญาตให้แอปแสดงการแจ้งเตือน"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"ใช้ฮาร์ดแวร์ชีวมิติ"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"อนุญาตให้แอปใช้ฮาร์ดแวร์ชีวมิติเพื่อตรวจสอบสิทธิ์"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"จัดการฮาร์ดแวร์ลายนิ้วมือ"</string>
diff --git a/core/res/res/values-tl-television/strings.xml b/core/res/res/values-tl-television/strings.xml
new file mode 100644
index 0000000..72443d1
--- /dev/null
+++ b/core/res/res/values-tl-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Naka-block ang mikropono"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Naka-block ang camera"</string>
+</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 6be37be..8ace595 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"tumawag at mamahala ng mga tawag sa telepono"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Mga sensor ng katawan"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"i-access ang data ng sensor tungkol sa iyong vital signs"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Mga Notification"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"magpakita ng mga notification"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Kunin ang content ng window"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Siyasatin ang nilalaman ng isang window kung saan ka nakikipag-ugnayan."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"I-on ang Explore by Touch"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Pinapayagan ang app na i-disable ang keylock at anumang nauugnay na seguridad sa password. Halimbawa, hindi pinapagana ng telepono ang keylock kapag nakakatanggap ng papasok na tawag sa telepono, pagkatapos ay muling pinapagana ang keylock kapag tapos na ang tawag."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"humiling ng pagiging kumplikado ng lock ng screen"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Pinapayagan ang app na malaman ang antas ng pagiging kumplikado ng lock ng screen (mataas, katamtaman, mababa, o wala), na nagsasaad ng posibleng hanay ng haba at uri ng lock ng screen. Puwede ring imungkahi ng app sa mga user na i-update nila ang lock ng screen sa isang partikular na antas ngunit malaya ang mga user na balewalain ito at mag-navigate palayo. Tandaang hindi naka-store bilang plaintext ang lock ng screen kaya hindi alam ng app ang eksaktong password."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"magpakita ng mga notification"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Pinapayagan ang app na magpakita ng mga notification"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"gumamit ng biometric hardware"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Pinapayagan ang app na gumamit ng biometric hardware para sa pag-authenticate"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"pamahalaan ang hardware ng fingerprint"</string>
diff --git a/core/res/res/values-tr-television/strings.xml b/core/res/res/values-tr-television/strings.xml
new file mode 100644
index 0000000..1d9d873
--- /dev/null
+++ b/core/res/res/values-tr-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofon engellenmiş"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera engellenmiş"</string>
+</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 51c3931..52614a8 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"telefon aramaları yapma ve yönetme"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Vücut sensörleri"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"hayati belirtilerinizle ilgili sensör verilerine erişme"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Bildirimler"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"bildirimleri göster"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Pencere içeriğini alma"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Etkileşim kurduğunuz pencerenin içeriğini inceler."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Dokunarak Keşfet\'i açma"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Uygulamaya, tuş kilidini ve ilişkili tüm şifreli güvenlik önlemlerini devre dışı bırakma izni verir. Örneğin, telefon, çağrı alındığında tuş kilidinin devre dışı bırakır ve sonra, görüşme bittiğinde kilidi yeniden etkinleştirir."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"ekran kilidi karmaşıklığı iste"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Uygulamanın, ekran kilidi karmaşıklık seviyesini (yüksek, orta, düşük veya yok) öğrenmesini sağlar. Ekran kilidi karmaşıklık seviyesi, ekran kilidinin olası uzunluk aralığını ve türünü gösterir. Uygulama, kullanıcılara ekran kilidini belirli bir seviyeye güncellemelerini de önerebilir, ancak kullanıcılar bunu istedikleri gibi yoksayabilir ve geçebilirler. Ekran kilidi şifrelenmemiş metin olarak saklanmadığı için uygulamanın şifreyi tam olarak bilmediğini unutmayın."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"bildirimleri göster"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Uygulamanın bildirimleri göstermesine izin verir"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"biyometrik donanım kullan"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Uygulamanın kimlik doğrulama için biyometrik donanım kullanmasına izin verir"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"parmak izi donanımını yönetme"</string>
diff --git a/core/res/res/values-uk-television/strings.xml b/core/res/res/values-uk-television/strings.xml
new file mode 100644
index 0000000..d04cd4f
--- /dev/null
+++ b/core/res/res/values-uk-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Мікрофон заблоковано"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Камеру заблоковано"</string>
+</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index c92aa08..934c51d 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -332,6 +332,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"телефонувати та керувати дзвінками"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Датчики на тілі"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"отримувати доступ до інформації датчиків про ваші життєві показники"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Сповіщення"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"показувати сповіщення"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Отримувати вміст вікна"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Перевіряти вміст вікна, з яким ви взаємодієте."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Увімкнути функцію дослідження дотиком"</string>
@@ -555,6 +557,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Дозволяє програмі вимикати блокування клавіатури та будь-який пов’язаний паролем захист. Наприклад: телефон вимикає блокування клавіатури під час отримання вхідного дзвінка, після закінчення якого блокування клавіатури відновлюється."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"запитувати рівень складності блокування екрана"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Дозволяє додатку визначати рівень складності способу блокування екрана (високий, середній, низький або нульовий). Враховуються кількість символів і тип блокування. Додаток також може пропонувати вибрати складніший тип блокування екрана, але це не обов’язково робити. Примітка: оскільки пароль зашифровано, додаток його не знає."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"показувати сповіщення"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Дозволяє додатку показувати сповіщення"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"використовувати біометричне апаратне забезпечення"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Додаток може використовувати біометричне апаратне забезпечення для автентифікації"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"керувати сканером відбитків пальців"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 0af79d3..5880de7 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -326,6 +326,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"فون کالز کریں اور ان کا نظم کریں"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"باڈی سینسرز"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"اپنی علامات حیات کے متعلق سنسر ڈیٹا تک رسائی حاصل کریں"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"ونڈو مواد بازیافت کرنے کی"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"کسی ایسی ونڈو کے مواد کا معائنہ کریں جس کے ساتھ آپ تعامل کر رہے ہیں۔"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"ٹچ کے ذریعے دریافت کریں کو آن کرنے کی"</string>
@@ -549,6 +553,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"ایپ کو کلیدی لاک اور کسی بھی متعلقہ پاس ورڈ سیکیورٹی کو غیر فعال کرنے کی اجازت دیتا ہے۔ مثلاً، کوئی آنے والی فون کال موصول ہونے کے وقت فون کلیدی لاک کو غیر فعال کرتا ہے، پھر کال پوری ہوجانے پر کلیدی لاک کو دوبارہ فعال کردیتا ہے۔"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"اسکرین لاک کی پیچیدگی کی درخواست کریں"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"اپپ کو اسکرین لاک کی پیچیدگی (بہت زیادہ، درمیانی، کم یا کوئی بھی نہیں) کو جاننے کی اجازت دیتی ہے، جو طوالت کی ممکنہ حد اور اسکرین لاک کی قسم کو بتاتی ہے۔ اپپ صارفین کو یہ مشوره بھی دے سکتی ہے کہ وہ اسکرین لاک کو مخصوص لیول تک اپ ڈیٹ کریں لیکن صارفین آزادانہ طور پر نظر انداز اور نیویگیٹ کر سکتے ہیں۔ نوٹ کریں کہ اسکرین لاک پلین ٹیکسٹ میں اسٹور نہیں کیا جاتا ہے اس لیے اپپ کو صحیح پاس ورڈ نہیں معلوم ہوتا ہے۔"</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"بایومیٹرک ہارڈ ویئر استعمال کریں"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"ایپ کو توثیق کے لیے بایومیٹرک ہارڈ ویئر استعمال کرنے کی اجازت دیتا ہے"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"فنگر پرنٹ ہارڈ ویئر کا نظم کریں"</string>
diff --git a/core/res/res/values-uz-television/strings.xml b/core/res/res/values-uz-television/strings.xml
new file mode 100644
index 0000000..31f1f77
--- /dev/null
+++ b/core/res/res/values-uz-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Mikrofon bloklangan"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Kamera bloklangan"</string>
+</resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index f200dbb..d4e2fcb 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"telefon qo‘ng‘iroqlarini amalga oshirish va boshqarish"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Tana sezgichlari"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"organizm holati haqidagi sezgich ma’lumotlariga kirish"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Bildirishnomalar"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"bildirishnomalarni chiqarish"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Oynadagi kontentni o‘qiydi"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Joriy oynadagi kontent mazmunini aniqlaydi."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Teginib o‘rganish xizmatini yoqadi"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Ilovaga ekran qulfini va har qanday parol yordamidagi xavfsizlik himoyalarini o‘chirishga ruxsat beradi. Masalan, kirish qo‘ng‘irog‘ida telefon ekran qulfini o‘chiradi va qo‘ng‘iroq tugashi bilan qulfni yoqadi."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"ekran qulfi qiyinligi darajasini talab qilish"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Ilova ekran qulfining qiyinlik darajasi (yuqori, oʻrta, past yoki hech qanday), jumladan, qulf turi va parol uzunligi haqida axborotga ruxsat oladi. Bundan tashqari, foydalanuvchilarga qulflash qiyinligi darajasini oshirish taklif etiladi. Bu tavsiyalar majburiy emas. Parollar ochiq matn sifatida saqlanmaydi va ilova uni ocha olmaydi."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"bildirishnomalarni chiqarish"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Ilovaga bildirishnomalarni chiqarish uchun ruxsat beradi"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"biometrik sensordan foydalanish"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Haqiqiylikni tekshirish uchun biometrik sensordan foydalanish imkonini beradi"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"barmoq izi skanerini boshqarish"</string>
diff --git a/core/res/res/values-vi-television/strings.xml b/core/res/res/values-vi-television/strings.xml
new file mode 100644
index 0000000..e5493bb
--- /dev/null
+++ b/core/res/res/values-vi-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Micrô bị chặn"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Máy ảnh bị chặn"</string>
+</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 178713a..872a175 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"thực hiện và quản lý cuộc gọi điện thoại"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Cảm biến cơ thể"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"truy cập dữ liệu cảm biến về dấu hiệu sinh tồn của bạn"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"Thông báo"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"hiển thị thông báo"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Truy xuất nội dung cửa sổ"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Kiểm tra nội dung của cửa sổ bạn đang tương tác."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Bật Khám phá bằng cách chạm"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Cho phép ứng dụng tắt khóa phím và bất kỳ bảo mật mật khẩu được liên kết nào. Ví dụ: điện thoại tắt khóa phím khi nhận được cuộc gọi đến, sau đó bật lại khóa phím khi cuộc gọi kết thúc."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"cần biết độ phức tạp của khóa màn hình"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Cho phép ứng dụng nắm được độ phức tạp của khóa màn hình (cao, trung bình, thấp hoặc không có). Mức độ này cho biết độ dài và loại khóa màn hình có thể có. Ngoài ra, ứng dụng có thể gợi ý người dùng cập nhật khóa màn hình lên một mức độ nhất định, nhưng người dùng có thể tùy ý bỏ qua và chuyển sang phần khác. Lưu ý rằng khóa màn hình không được lưu dưới dạng văn bản thuần túy, vì vậy, ứng dụng sẽ không biết mật khẩu chính xác."</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"hiển thị thông báo"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"Cho phép ứng dụng hiển thị thông báo"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"sử dụng phần cứng sinh trắc học"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Cho phép ứng dụng dùng phần cứng sinh trắc học để xác thực"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"quản lý phần cứng vân tay"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index b9d5654..8bbcf69 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"拨打电话和管理通话"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"身体传感器"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"访问与您的生命体征相关的传感器数据"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"通知"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"显示通知"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"检索窗口内容"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"检测您与之互动的窗口的内容。"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"启用触摸浏览"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"允许该应用停用键锁以及任何关联的密码安全措施。例如,让手机在接听来电时停用键锁,在通话结束后重新启用键锁。"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"请求屏幕锁定复杂度"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"允许应用了解屏幕锁定复杂度(高、中、低或无),即屏幕锁定的可能长度范围和类型。应用还可以建议用户将屏幕锁定更新为特定复杂度,但用户可以随意选择忽略该建议并离开应用。请注意,系统不会以纯文字形式存储屏幕锁定选项的内容,因此应用不会知道确切密码。"</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"显示通知"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"允许该应用显示通知"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"使用生物特征硬件"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"允许该应用使用生物特征硬件进行身份验证"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"管理指纹硬件"</string>
diff --git a/core/res/res/values-zh-rHK-television/strings.xml b/core/res/res/values-zh-rHK-television/strings.xml
new file mode 100644
index 0000000..57ded31
--- /dev/null
+++ b/core/res/res/values-zh-rHK-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"已封鎖麥克風"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"已封鎖攝錄機"</string>
+</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index b87d7c0..7c276a5 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"撥打電話及管理通話"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"人體感應器"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"存取與您生命體徵相關的感應器資料"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"通知"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"顯示通知"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"擷取視窗內容"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"檢查您使用中的視窗內容。"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"開啟「輕觸探索」功能"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"允許應用程式停用按鍵鎖定以及其他相關的密碼安全措施。例如:手機收到來電時停用按鍵鎖定,通話結束後重新啟用按鍵鎖定。"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"要求螢幕鎖定複雜程度"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"允許應用程式瞭解螢幕鎖定的複雜程度 (高、中、低或無),這項資料說明了螢幕鎖定的可能長度範圍和類型。應用程式亦能建議使用者將螢幕鎖定更新至某個複雜程度,但使用者可以隨意忽略並離開。請注意,螢幕鎖定並非以純文字儲存,因此應用程式不知道確切的密碼。"</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"顯示通知"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"允許應用程式顯示通知"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"使用生物識別硬件"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"允許應用程式使用生物識別硬件驗證"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"管理指紋硬件"</string>
diff --git a/core/res/res/values-zh-rTW-television/strings.xml b/core/res/res/values-zh-rTW-television/strings.xml
new file mode 100644
index 0000000..0b0458b
--- /dev/null
+++ b/core/res/res/values-zh-rTW-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"已封鎖麥克風"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"已封鎖攝影機"</string>
+</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 71bcf00..afc1392 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -326,6 +326,8 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"撥打電話及管理通話"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"人體感測器"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"存取與你生命徵象相關的感應器資料"</string>
+ <string name="permgrouplab_notifications" msgid="5472972361980668884">"通知"</string>
+ <string name="permgroupdesc_notifications" msgid="4608679556801506580">"顯示通知"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"擷取視窗內容"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"檢查你存取的視窗內容。"</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"啟用輕觸探索功能"</string>
@@ -549,6 +551,8 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"允許應用程式停用按鍵鎖定以及其他相關的密碼安全性功能。例如:手機收到來電時停用按鍵鎖定,通話結束後重新啟用按鍵鎖定。"</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"要求螢幕鎖定的複雜度"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"允許應用程式記住螢幕鎖定的複雜度 (高、中、低或無),即螢幕鎖定的可能長度範圍和類型。這樣一來,應用程式還能建議使用者將螢幕鎖定更新為特定複雜度,但使用者可選擇忽略建議並離開應用程式。請注意,系統不會以純文字格式儲存螢幕鎖定選項的內容,因此應用程式不會知道確切密碼。"</string>
+ <string name="permlab_postNotification" msgid="4875401198597803658">"顯示通知"</string>
+ <string name="permdesc_postNotification" msgid="5974977162462877075">"允許應用程式顯示通知"</string>
<string name="permlab_useBiometric" msgid="6314741124749633786">"使用生物特徵硬體"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"允許應用程式使用生物特徵硬體進行驗證"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"管理指紋硬體"</string>
diff --git a/core/res/res/values-zu-television/strings.xml b/core/res/res/values-zu-television/strings.xml
new file mode 100644
index 0000000..ddf7ea9
--- /dev/null
+++ b/core/res/res/values-zu-television/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="7002619958660406548">"Imakrofoni ivinjiwe"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="2131954635322568179">"Ikhamera ivinjiwe"</string>
+</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 4c7624e..ad9c34a 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -326,6 +326,10 @@
<string name="permgroupdesc_phone" msgid="270048070781478204">"yenza uphinde uphathe amakholi wefoni"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Izinzwa zomzimba"</string>
<string name="permgroupdesc_sensors" msgid="2610631290633747752">"finyelela idatha yesizweli mayelana nezimpawu zakho ezibalulekile"</string>
+ <!-- no translation found for permgrouplab_notifications (5472972361980668884) -->
+ <skip />
+ <!-- no translation found for permgroupdesc_notifications (4608679556801506580) -->
+ <skip />
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Thola okuqukethwe kwewindi"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Hlola okuqukethwe kwewindi ohlanganyela nalo."</string>
<string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Vula ukuhlola ngokuthinta"</string>
@@ -549,6 +553,10 @@
<string name="permdesc_disableKeyguard" msgid="3223710003098573038">"Ivumela uhlelo lokusebenza ukukhubaza ukuvala ukhiye nanoma yikuphi ukuphepha kwephasiwedi okuhlobene. Isibonelo, ifoni ikhubaza ukuvala ukhiye lapho ithola ikholi yefoni engenayo, bese inike amandla kabusha ukuvala ukhiye lapho ikholi isiqedile."</string>
<string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"cela ubunkimbinkimbi kokukhiya isikrini"</string>
<string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"Ivumela uhlelo lokusebenza ukuthi lifunde ileveli yobunkimbinkimbi bokukhiya isikrini (okuphezulu, okuphakathi nendawo, okuphansi noma lutho), obubonisa ibanga elingaba khona lobude nohlobo lokukhiya isikrini Uhlelo lokusebenza lungaphinda luphakamise kubasebenzisi ukuthi babuyekeze ukukhiya isikrini kuleveli elithile kodwa abasebenzisi bangaziba ngokukhululekile baphinde bazulazule nje. Qaphela ukuthi ukukhiya isikrini akugcinwa kumbhalo osobala ukuze uhlelo lokusebenza lungayazi iphasiwedi enembile."</string>
+ <!-- no translation found for permlab_postNotification (4875401198597803658) -->
+ <skip />
+ <!-- no translation found for permdesc_postNotification (5974977162462877075) -->
+ <skip />
<string name="permlab_useBiometric" msgid="6314741124749633786">"sebenzisa izingxenyekazi zekhompyutha ze-biometric"</string>
<string name="permdesc_useBiometric" msgid="7502858732677143410">"Ivumela uhlelo lokusebenza ukuthi lusebenzise izingxenyekazi zekhompyutha ze-biometric ukuze kuqinisekiswe"</string>
<string name="permlab_manageFingerprint" msgid="7432667156322821178">"phatha izingxenyekazi zekhompyutha zezigxivizo zeminwe"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 077e568..676546b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8372,8 +8372,8 @@
<attr name="supportsAmbientMode" format="boolean" />
<!-- Indicates that this wallpaper service should receive zoom transition updates when
- changing the device state (e.g. when folding or unfolding a foldable device).
- When this value is set to true
+ changing the display state of the device (e.g. when folding or unfolding
+ a foldable device). When this value is set to true
{@link android.service.wallpaper.WallpaperService.Engine} could receive zoom updates
before or after changing the device state. Wallpapers receive zoom updates using
{@link android.service.wallpaper.WallpaperService.Engine#onZoomChanged(float)} and
@@ -8381,8 +8381,8 @@
{@link android.service.wallpaper.WallpaperService.Engine} is created and not destroyed.
Default value is true.
Corresponds to
- {@link android.app.WallpaperInfo#shouldUseDefaultDeviceStateChangeTransition()} -->
- <attr name="shouldUseDefaultDeviceStateChangeTransition" format="boolean" />
+ {@link android.app.WallpaperInfo#shouldUseDefaultDisplayStateChangeTransition()} -->
+ <attr name="shouldUseDefaultDisplayStateChangeTransition" format="boolean" />
<!-- Uri that specifies a settings Slice for this wallpaper. -->
<attr name="settingsSliceUri" format="string"/>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index b191584..59d6005 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -246,37 +246,37 @@
<!-- Lightest shade of the accent color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_0">#ffffff</color>
- <!-- Shade of the accent system color at 99% lightness.
+ <!-- Shade of the accent system color at 99% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_10">#F9FCFF</color>
- <!-- Shade of the accent system color at 95% lightness.
+ <!-- Shade of the accent system color at 95% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_50">#E0F3FF</color>
- <!-- Shade of the accent system color at 90% lightness.
+ <!-- Shade of the accent system color at 90% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_100">#C1E8FF</color>
- <!-- Shade of the accent system color at 80% lightness.
+ <!-- Shade of the accent system color at 80% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_200">#76D1FF</color>
- <!-- Shade of the accent system color at 70% lightness.
+ <!-- Shade of the accent system color at 70% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_300">#4BB6E8</color>
- <!-- Shade of the accent system color at 60% lightness.
+ <!-- Shade of the accent system color at 60% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_400">#219BCC</color>
- <!-- Shade of the accent system color at 49% lightness.
+ <!-- Shade of the accent system color at 49.6% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_500">#007FAC</color>
- <!-- Shade of the accent system color at 40% lightness.
+ <!-- Shade of the accent system color at 40% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_600">#00668B</color>
- <!-- Shade of the accent system color at 30% lightness.
+ <!-- Shade of the accent system color at 30% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_700">#004C69</color>
- <!-- Shade of the accent system color at 20% lightness.
+ <!-- Shade of the accent system color at 20% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_800">#003549</color>
- <!-- Shade of the accent system color at 10% lightness.
+ <!-- Shade of the accent system color at 10% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent1_900">#001E2C</color>
<!-- Darkest shade of the accent color used by the system. Black.
@@ -286,37 +286,37 @@
<!-- Lightest shade of the secondary accent color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_0">#ffffff</color>
- <!-- Shade of the secondary accent system color at 99% lightness.
+ <!-- Shade of the secondary accent system color at 99% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_10">#F9FCFF</color>
- <!-- Shade of the secondary accent system color at 95% lightness.
+ <!-- Shade of the secondary accent system color at 95% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_50">#E0F3FF</color>
- <!-- Shade of the secondary accent system color at 90% lightness.
+ <!-- Shade of the secondary accent system color at 90% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_100">#D1E5F4</color>
- <!-- Shade of the secondary accent system color at 80% lightness.
+ <!-- Shade of the secondary accent system color at 80% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_200">#B5CAD7</color>
- <!-- Shade of the secondary accent system color at 70% lightness.
+ <!-- Shade of the secondary accent system color at 70% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_300">#9AAEBB</color>
- <!-- Shade of the secondary accent system color at 60% lightness.
+ <!-- Shade of the secondary accent system color at 60% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_400">#8094A0</color>
- <!-- Shade of the secondary accent system color at 49% lightness.
+ <!-- Shade of the secondary accent system color at 49.6% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_500">#657985</color>
- <!-- Shade of the secondary accent system color at 40% lightness.
+ <!-- Shade of the secondary accent system color at 40% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_600">#4E616C</color>
- <!-- Shade of the secondary accent system color at 30% lightness.
+ <!-- Shade of the secondary accent system color at 30% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_700">#374955</color>
- <!-- Shade of the secondary accent system color at 20% lightness.
+ <!-- Shade of the secondary accent system color at 20% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_800">#20333D</color>
- <!-- Shade of the secondary accent system color at 10% lightness.
+ <!-- Shade of the secondary accent system color at 10% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent2_900">#091E28</color>
<!-- Darkest shade of the secondary accent color used by the system. Black.
@@ -326,37 +326,37 @@
<!-- Lightest shade of the tertiary accent color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_0">#ffffff</color>
- <!-- Shade of the tertiary accent system color at 99% lightness.
+ <!-- Shade of the tertiary accent system color at 99% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_10">#FFFBFF</color>
- <!-- Shade of the tertiary accent system color at 95% lightness.
+ <!-- Shade of the tertiary accent system color at 95% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_50">#F5EEFF</color>
- <!-- Shade of the tertiary accent system color at 90% lightness.
+ <!-- Shade of the tertiary accent system color at 90% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_100">#E6DEFF</color>
- <!-- Shade of the tertiary accent system color at 80% lightness.
+ <!-- Shade of the tertiary accent system color at 80% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_200">#CAC1EA</color>
- <!-- Shade of the tertiary accent system color at 70% lightness.
+ <!-- Shade of the tertiary accent system color at 70% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_300">#AEA6CE</color>
- <!-- Shade of the tertiary accent system color at 60% lightness.
+ <!-- Shade of the tertiary accent system color at 60% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_400">#938CB1</color>
- <!-- Shade of the tertiary accent system color at 49% lightness.
+ <!-- Shade of the tertiary accent system color at 49% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_500">#787296</color>
- <!-- Shade of the tertiary accent system color at 40% lightness.
+ <!-- Shade of the tertiary accent system color at 40% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_600">#605A7C</color>
- <!-- Shade of the tertiary accent system color at 30% lightness.
+ <!-- Shade of the tertiary accent system color at 30% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_700">#484264</color>
- <!-- Shade of the tertiary accent system color at 20% lightness.
+ <!-- Shade of the tertiary accent system color at 20% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_800">#322C4C</color>
- <!-- Shade of the tertiary accent system color at 10% lightness.
+ <!-- Shade of the tertiary accent system color at 10% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_accent3_900">#1D1736</color>
<!-- Darkest shade of the tertiary accent color used by the system. Black.
@@ -366,37 +366,37 @@
<!-- Lightest shade of the neutral color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_0">#ffffff</color>
- <!-- Shade of the neutral system color at 99% lightness.
+ <!-- Shade of the neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_10">#FCFCFF</color>
- <!-- Shade of the neutral system color at 95% lightness.
+ <!-- Shade of the neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_50">#F0F0F3</color>
- <!-- Shade of the neutral system color at 90% lightness.
+ <!-- Shade of the neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_100">#E1E3E5</color>
- <!-- Shade of the neutral system color at 80% lightness.
+ <!-- Shade of the neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_200">#C5C7C9</color>
- <!-- Shade of the neutral system color at 70% lightness.
+ <!-- Shade of the neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_300">#AAABAE</color>
- <!-- Shade of the neutral system color at 60% lightness.
+ <!-- Shade of the neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_400">#8F9193</color>
- <!-- Shade of the neutral system color at 49% lightness.
+ <!-- Shade of the neutral system color at 49% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_500">#747679</color>
- <!-- Shade of the neutral system color at 40% lightness.
+ <!-- Shade of the neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_600">#5C5F61</color>
- <!-- Shade of the neutral system color at 30% lightness.
+ <!-- Shade of the neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_700">#454749</color>
- <!-- Shade of the neutral system color at 20% lightness.
+ <!-- Shade of the neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_800">#2E3133</color>
- <!-- Shade of the neutral system color at 10% lightness.
+ <!-- Shade of the neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral1_900">#191C1E</color>
<!-- Darkest shade of the neutral color used by the system. Black.
@@ -406,37 +406,37 @@
<!-- Lightest shade of the secondary neutral color used by the system. White.
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_0">#ffffff</color>
- <!-- Shade of the secondary neutral system color at 99% lightness.
+ <!-- Shade of the secondary neutral system color at 99% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_10">#F9FCFF</color>
- <!-- Shade of the secondary neutral system color at 95% lightness.
+ <!-- Shade of the secondary neutral system color at 95% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_50">#EBF1F8</color>
- <!-- Shade of the secondary neutral system color at 90% lightness.
+ <!-- Shade of the secondary neutral system color at 90% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_100">#DCE3E9</color>
- <!-- Shade of the secondary neutral system color at 80% lightness.
+ <!-- Shade of the secondary neutral system color at 80% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_200">#C0C7CD</color>
- <!-- Shade of the secondary neutral system color at 70% lightness.
+ <!-- Shade of the secondary neutral system color at 70% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_300">#A5ACB2</color>
- <!-- Shade of the secondary neutral system color at 60% lightness.
+ <!-- Shade of the secondary neutral system color at 60% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_400">#8A9297</color>
- <!-- Shade of the secondary neutral system color at 49% lightness.
+ <!-- Shade of the secondary neutral system color at 49% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_500">#70777C</color>
- <!-- Shade of the secondary neutral system color at 40% lightness.
+ <!-- Shade of the secondary neutral system color at 40% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_600">#585F65</color>
- <!-- Shade of the secondary neutral system color at 30% lightness.
+ <!-- Shade of the secondary neutral system color at 30% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_700">#40484D</color>
- <!-- Shade of the secondary neutral system color at 20% lightness.
+ <!-- Shade of the secondary neutral system color at 20% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_800">#2A3136</color>
- <!-- Shade of the secondary neutral system color at 10% lightness.
+ <!-- Shade of the secondary neutral system color at 10% perceptual luminance (L* in L*a*b* color space).
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_neutral2_900">#161C20</color>
<!-- Darkest shade of the secondary neutral color used by the system. Black.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7e87baa..40bd4ca 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2316,6 +2316,15 @@
<!-- Type of the tap sensor. Empty if tap is not supported. -->
<string name="config_dozeTapSensorType" translatable="false"></string>
+ <!-- Type of the ambient tap sensor per device posture (defined by WM Jetpack posture).
+ Unspecified values use config_dozeTapSensor -->
+ <string-array name="config_dozeTapSensorPostureMapping" translatable="false">
+ <item></item> <!-- UNKNOWN -->
+ <item></item> <!-- CLOSED -->
+ <item></item> <!-- HALF_OPENED -->
+ <item></item> <!-- OPENED -->
+ </string-array>
+
<!-- Type of the long press sensor. Empty if long press is not supported. -->
<string name="config_dozeLongPressSensorType" translatable="false"></string>
@@ -4527,6 +4536,13 @@
If non-positive, then the refresh rate is unchanged even if thresholds are configured. -->
<integer name="config_fixedRefreshRateInHighZone">0</integer>
+ <!-- Default refresh rate while the device has high brightness mode enabled for Sunlight.
+ This value overrides values from DisplayDeviceConfig -->
+ <integer name="config_defaultRefreshRateInHbmSunlight">0</integer>
+
+ <!-- Default refresh rate while the device has high brightness mode enabled for HDR. -->
+ <integer name="config_defaultRefreshRateInHbmHdr">0</integer>
+
<!-- The type of the light sensor to be used by the display framework for things like
auto-brightness. If unset, then it just gets the default sensor of type TYPE_LIGHT. -->
<string name="config_displayLightSensorType" translatable="false" />
@@ -4918,16 +4934,15 @@
device orientation. -->
<bool name="config_letterboxIsReachabilityEnabled">false</bool>
- <!-- Default horizonal position of a center of the letterboxed app window when reachability is
- enabled and an app is fullscreen in landscape device orientation.
- 0 corresponds to the left side of the screen and 1 to the right side. If given value < 0.0
- or > 1, it is ignored and right positionis used (1.0). The position multiplier is changed
- to a symmetrical value computed as (1 - current multiplier) after each double tap in the
- letterbox area. -->
- <item name="config_letterboxDefaultPositionMultiplierForReachability"
- format="float" type="dimen">
- 0.9
- </item>
+ <!-- Default horizonal position of the letterboxed app window when reachability is
+ enabled and an app is fullscreen in landscape device orientation. When reachability is
+ enabled, the position can change between left, center and right. This config defines the
+ default one:
+ - Option 0 - Left.
+ - Option 1 - Center.
+ - Option 2 - Right.
+ If given value is outside of this range, the option 1 (center) is assummed. -->
+ <integer name="config_letterboxDefaultPositionForReachability">1</integer>
<!-- If true, hide the display cutout with display area -->
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
@@ -5293,4 +5308,9 @@
<item>@dimen/rounded_corner_radius_bottom_adjustment</item>
<item>@dimen/secondary_rounded_corner_radius_bottom_adjustment</item>
</array>
+
+ <!-- Shape of the work badge icon for viewport size 24. -->
+ <string name="config_work_badge_path_24" translatable="false">
+ M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2z
+ </string>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index a4151c7..45201a9 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -60,6 +60,9 @@
<!-- How much we expand the touchable region of the status bar below the notch to catch touches
that just start below the notch. -->
<dimen name="display_cutout_touchable_region_size">12dp</dimen>
+ <!-- The default margin used in immersive mode to capture the start of a swipe gesture from the
+ edge of the screen to show the system bars. -->
+ <dimen name="system_gestures_start_threshold">24dp</dimen>
<!-- Height of the bottom navigation bar frame; this is different than navigation_bar_height
where that is the height reported to all the other windows to resize themselves around the
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index cf03916..fea3a6e 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3222,7 +3222,7 @@
<eat-comment />
<staging-public-group type="attr" first-id="0x01ff0000">
- <public name="shouldUseDefaultDeviceStateChangeTransition" />
+ <public name="shouldUseDefaultDisplayStateChangeTransition" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01fe0000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4c1cc87..dbb2b1a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -837,6 +837,11 @@
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgroupdesc_sensors">access sensor data about your vital signs</string>
+ <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]-->
+ <string name="permgrouplab_notifications">Notifications</string>
+ <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]-->
+ <string name="permgroupdesc_notifications">show notifications</string>
+
<!-- Title for the capability of an accessibility service to retrieve window content. -->
<string name="capability_title_canRetrieveWindowContent">Retrieve window content</string>
<!-- Description for the capability of an accessibility service to retrieve window content. -->
@@ -1519,6 +1524,11 @@
</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+ <string name="permlab_postNotification">show notifications</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+ <string name="permdesc_postNotification">Allows the app to show notifications</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
<string name="permlab_useBiometric">use biometric hardware</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this.[CHAR_LIMIT=NONE] -->
<string name="permdesc_useBiometric">Allows the app to use biometric hardware for authentication</string>
@@ -4319,6 +4329,9 @@
doesn't support the work profile. [CHAR LIMIT=100] -->
<string name="activity_resolver_work_profiles_support">%1$s doesn\'t support work profile</string>
+ <!-- DO NOT TRANSLATE -->
+ <string name="default_audio_route_id">default_audio_route</string>
+
<!-- Name of the default audio route for tablets when nothing
is connected to a headphone or other wired audio output jack. [CHAR LIMIT=50] -->
<string name="default_audio_route_name" product="tablet">Tablet</string>
@@ -4347,6 +4360,9 @@
<!-- Name of the default audio route category. [CHAR LIMIT=50] -->
<string name="default_audio_route_category_name">System</string>
+ <!-- DO NOT TRANSLATE -->
+ <string name="bluetooth_a2dp_audio_route_id">bluetooth_a2dp_audio_route</string>
+
<!-- Description of the bluetooth a2dp audio route. [CHAR LIMIT=50] -->
<string name="bluetooth_a2dp_audio_route_name">Bluetooth audio</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f9d150e..86cf04e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1083,6 +1083,7 @@
<java-symbol type="string" name="granularity_label_word" />
<java-symbol type="string" name="granularity_label_link" />
<java-symbol type="string" name="granularity_label_line" />
+ <java-symbol type="string" name="default_audio_route_id" />
<java-symbol type="string" name="default_audio_route_name" />
<java-symbol type="string" name="default_audio_route_name_dock_speakers" />
<java-symbol type="string" name="default_audio_route_name_hdmi" />
@@ -1664,6 +1665,7 @@
<java-symbol type="string" name="media_route_chooser_title" />
<java-symbol type="string" name="media_route_chooser_title_for_remote_display" />
<java-symbol type="string" name="media_route_controller_disconnect" />
+ <java-symbol type="string" name="bluetooth_a2dp_audio_route_id" />
<java-symbol type="string" name="bluetooth_a2dp_audio_route_name" />
<java-symbol type="dimen" name="config_minScalingSpan" />
@@ -1747,6 +1749,7 @@
<java-symbol type="dimen" name="taskbar_frame_height" />
<java-symbol type="dimen" name="status_bar_height" />
<java-symbol type="dimen" name="display_cutout_touchable_region_size" />
+ <java-symbol type="dimen" name="system_gestures_start_threshold" />
<java-symbol type="dimen" name="quick_qs_offset_height" />
<java-symbol type="drawable" name="ic_jog_dial_sound_off" />
<java-symbol type="drawable" name="ic_jog_dial_sound_on" />
@@ -3257,6 +3260,7 @@
<java-symbol type="string" name="config_dozeDoubleTapSensorType" />
<java-symbol type="string" name="config_dozeTapSensorType" />
+ <java-symbol type="array" name="config_dozeTapSensorPostureMapping" />
<java-symbol type="bool" name="config_dozePulsePickup" />
<!-- Used for MimeIconUtils. -->
@@ -3965,6 +3969,8 @@
<java-symbol type="integer" name="config_defaultRefreshRateInZone" />
<java-symbol type="array" name="config_brightnessThresholdsOfPeakRefreshRate" />
<java-symbol type="array" name="config_ambientThresholdsOfPeakRefreshRate" />
+ <java-symbol type="integer" name="config_defaultRefreshRateInHbmSunlight" />
+ <java-symbol type="integer" name="config_defaultRefreshRateInHbmHdr" />
<!-- For fixed refresh rate displays in high brightness-->
<java-symbol type="integer" name="config_fixedRefreshRateInHighZone" />
@@ -4261,7 +4267,7 @@
<java-symbol type="color" name="config_letterboxBackgroundColor" />
<java-symbol type="dimen" name="config_letterboxHorizontalPositionMultiplier" />
<java-symbol type="bool" name="config_letterboxIsReachabilityEnabled" />
- <java-symbol type="dimen" name="config_letterboxDefaultPositionMultiplierForReachability" />
+ <java-symbol type="integer" name="config_letterboxDefaultPositionForReachability" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
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 6884f13d..0000000
--- a/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java
+++ /dev/null
@@ -1,65 +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}});
- }
-}
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/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 79e7c50..2018836 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@
// The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
// See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
// and assertAccessibilityNodeInfoCleared in that class.
- private static final int NUM_MARSHALLED_PROPERTIES = 39;
+ private static final int NUM_MARSHALLED_PROPERTIES = 40;
/**
* The number of properties that are purposely not marshalled
diff --git a/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java b/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
index d800c2c..b34554c 100644
--- a/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/ProcLocksReaderTest.java
@@ -16,7 +16,6 @@
package com.android.internal.os;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.Context;
@@ -33,11 +32,14 @@
import java.io.File;
import java.nio.file.Files;
+import java.util.ArrayList;
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ProcLocksReaderTest {
+public class ProcLocksReaderTest implements
+ ProcLocksReader.ProcLocksReaderCallback {
private File mProcDirectory;
+ private ArrayList<Integer> mPids = new ArrayList<>();
@Before
public void setUp() {
@@ -55,14 +57,12 @@
String simpleLocks =
"1: POSIX ADVISORY READ 18403 fd:09:9070 1073741826 1073742335\n" +
"2: POSIX ADVISORY WRITE 18292 fd:09:34062 0 EOF\n";
- assertFalse(runHasFileLocks(simpleLocks, 18402));
- assertFalse(runHasFileLocks(simpleLocks, 18404));
- assertTrue(runHasFileLocks(simpleLocks, 18403));
- assertTrue(runHasFileLocks(simpleLocks, 18292));
+ runHandleBlockingFileLocks(simpleLocks);
+ assertTrue(mPids.isEmpty());
}
@Test
- public void testRunBlockedLocks() throws Exception {
+ public void testRunBlockingLocks() throws Exception {
String blockedLocks =
"1: POSIX ADVISORY READ 18403 fd:09:9070 1073741826 1073742335\n" +
"2: POSIX ADVISORY WRITE 18292 fd:09:34062 0 EOF\n" +
@@ -70,21 +70,43 @@
"2: -> POSIX ADVISORY WRITE 18293 fd:09:34062 0 EOF\n" +
"3: POSIX ADVISORY READ 3888 fd:09:13992 128 128\n" +
"4: POSIX ADVISORY READ 3888 fd:09:14230 1073741826 1073742335\n";
- assertFalse(runHasFileLocks(blockedLocks, 18402));
- assertFalse(runHasFileLocks(blockedLocks, 18404));
- assertTrue(runHasFileLocks(blockedLocks, 18403));
- assertTrue(runHasFileLocks(blockedLocks, 18292));
-
- assertFalse(runHasFileLocks(blockedLocks, 18291));
- assertFalse(runHasFileLocks(blockedLocks, 18293));
- assertTrue(runHasFileLocks(blockedLocks, 3888));
+ runHandleBlockingFileLocks(blockedLocks);
+ assertTrue(mPids.remove(0).equals(18292));
+ assertTrue(mPids.isEmpty());
}
- private boolean runHasFileLocks(String fileContents, int pid) throws Exception {
+ @Test
+ public void testRunMultipleBlockingLocks() throws Exception {
+ String blockedLocks =
+ "1: POSIX ADVISORY READ 18403 fd:09:9070 1073741826 1073742335\n" +
+ "2: POSIX ADVISORY WRITE 18292 fd:09:34062 0 EOF\n" +
+ "2: -> POSIX ADVISORY WRITE 18291 fd:09:34062 0 EOF\n" +
+ "2: -> POSIX ADVISORY WRITE 18293 fd:09:34062 0 EOF\n" +
+ "3: POSIX ADVISORY READ 3888 fd:09:13992 128 128\n" +
+ "4: FLOCK ADVISORY WRITE 3840 fe:01:5111809 0 EOF\n" +
+ "4: -> FLOCK ADVISORY WRITE 3841 fe:01:5111809 0 EOF\n" +
+ "5: POSIX ADVISORY READ 3888 fd:09:14230 1073741826 1073742335\n";
+ runHandleBlockingFileLocks(blockedLocks);
+ assertTrue(mPids.remove(0).equals(18292));
+ assertTrue(mPids.remove(0).equals(3840));
+ assertTrue(mPids.isEmpty());
+ }
+
+ private void runHandleBlockingFileLocks(String fileContents) throws Exception {
File tempFile = File.createTempFile("locks", null, mProcDirectory);
Files.write(tempFile.toPath(), fileContents.getBytes());
- boolean result = new ProcLocksReader(tempFile.toString()).hasFileLocks(pid);
+ mPids.clear();
+ new ProcLocksReader(tempFile.toString()).handleBlockingFileLocks(this);
Files.delete(tempFile.toPath());
- return result;
+ }
+
+ /**
+ * Call the callback function of handleBlockingFileLocks().
+ *
+ * @param pid Each process that hold file locks blocking other processes.
+ */
+ @Override
+ public void onBlockingFileLock(int pid) {
+ mPids.add(pid);
}
}
diff --git a/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java b/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
index 0532628..b932760 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ProcFileReaderTest.java
@@ -19,8 +19,11 @@
import android.test.AndroidTestCase;
import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
/**
* Tests for {@link ProcFileReader}.
@@ -206,6 +209,41 @@
assertFalse(reader.hasMoreData());
}
+ public void testRewind() throws Exception {
+ final ProcFileReader reader = buildReader("abc\n");
+
+ assertEquals("abc", reader.nextString());
+ reader.finishLine();
+ assertFalse(reader.hasMoreData());
+
+ reader.rewind();
+ assertTrue(reader.hasMoreData());
+
+ assertEquals("abc", reader.nextString());
+ reader.finishLine();
+ assertFalse(reader.hasMoreData());
+ }
+
+
+ public void testRewindFileInputStream() throws Exception {
+ File tempFile = File.createTempFile("procfile", null, null);
+ Files.write(tempFile.toPath(), "abc\n".getBytes(StandardCharsets.US_ASCII));
+ final ProcFileReader reader = new ProcFileReader(new FileInputStream(tempFile));
+
+ assertEquals("abc", reader.nextString());
+ reader.finishLine();
+ assertFalse(reader.hasMoreData());
+
+ reader.rewind();
+ assertTrue(reader.hasMoreData());
+
+ assertEquals("abc", reader.nextString());
+ reader.finishLine();
+ assertFalse(reader.hasMoreData());
+
+ Files.delete(tempFile.toPath());
+ }
+
private static ProcFileReader buildReader(String string) throws IOException {
return buildReader(string, 2048);
}
diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp
deleted file mode 100644
index d1e4322..0000000
--- a/data/etc/car/Android.bp
+++ /dev/null
@@ -1,209 +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.
-
-// Privapp permission whitelist files
-
-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"],
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.carsystemui",
- system_ext_specific: true,
- sub_dir: "permissions",
- src: "com.android.carsystemui.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_android.car.cluster.loggingrenderer",
- sub_dir: "permissions",
- src: "android.car.cluster.loggingrenderer.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_android.car.cluster.sample",
- sub_dir: "permissions",
- src: "android.car.cluster.sample.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_android.car.cluster",
- sub_dir: "permissions",
- src: "android.car.cluster.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_android.car.usb.handler",
- sub_dir: "permissions",
- src: "android.car.usb.handler.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.carlauncher",
- sub_dir: "permissions",
- src: "com.android.car.carlauncher.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.dialer",
- sub_dir: "permissions",
- src: "com.android.car.dialer.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.hvac",
- sub_dir: "permissions",
- src: "com.android.car.hvac.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.media",
- sub_dir: "permissions",
- src: "com.android.car.media.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.notification",
- sub_dir: "permissions",
- src: "com.android.car.notification.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.radio",
- sub_dir: "permissions",
- src: "com.android.car.radio.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.settings",
- sub_dir: "permissions",
- src: "com.android.car.settings.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.themeplayground",
- sub_dir: "permissions",
- src: "com.android.car.themeplayground.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car",
- sub_dir: "permissions",
- src: "com.android.car.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.bugreport",
- sub_dir: "permissions",
- src: "com.android.car.bugreport.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.google.android.car.adaslocation",
- sub_dir: "permissions",
- src: "com.google.android.car.adaslocation.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.google.android.car.kitchensink",
- sub_dir: "permissions",
- src: "com.google.android.car.kitchensink.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.developeroptions",
- sub_dir: "permissions",
- src: "com.android.car.developeroptions.xml",
- filename_from_src: true,
- system_ext_specific: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.rotary",
- sub_dir: "permissions",
- src: "com.android.car.rotary.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.ui.paintbooth",
- sub_dir: "permissions",
- src: "com.android.car.ui.paintbooth.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.provision",
- system_ext_specific: true,
- sub_dir: "permissions",
- src: "com.android.car.provision.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "privapp_allowlist_com.google.android.car.networking.preferenceupdater",
- sub_dir: "permissions",
- src: "com.google.android.car.networking.preferenceupdater.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.carshell",
- sub_dir: "permissions",
- src: "com.android.car.shell.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.activityresolver",
- sub_dir: "permissions",
- src: "com.android.car.activityresolver.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.cluster.home",
- sub_dir: "permissions",
- src: "com.android.car.cluster.home.xml",
- filename_from_src: true,
-}
-
-prebuilt_etc {
- name: "allowed_privapp_com.android.car.messenger",
- sub_dir: "permissions",
- src: "com.android.car.messenger.xml",
- filename_from_src: true,
-}
diff --git a/data/etc/car/CleanSpec.mk b/data/etc/car/CleanSpec.mk
deleted file mode 100644
index 18f7d34..0000000
--- a/data/etc/car/CleanSpec.mk
+++ /dev/null
@@ -1,50 +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.
-#
-
-# If you don't need to do a full clean build but would like to touch
-# a file or delete some intermediate files, add a clean step to the end
-# of the list. These steps will only be run once, if they haven't been
-# run before.
-#
-# E.g.:
-# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
-# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
-#
-# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
-# files that are missing or have been moved.
-#
-# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
-# Use $(OUT_DIR) to refer to the "out" directory.
-#
-# If you need to re-do something that's already mentioned, just copy
-# the command and add it to the bottom of the list. E.g., if a change
-# that you made last week required touching a file and a change you
-# made today requires touching the same file, just copy the old
-# touch step and add it to the end of the list.
-#
-# *****************************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER
-# *****************************************************************
-
-# For example:
-#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
-#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
-#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
-#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.car.developeroptions.xml)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.car.developeroptions.xml)
-# ******************************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
-# ******************************************************************
diff --git a/data/etc/car/OWNERS b/data/etc/car/OWNERS
deleted file mode 100644
index 09e257c..0000000
--- a/data/etc/car/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include platform/packages/services/Car:/OWNERS
diff --git a/data/etc/car/android.car.cluster.loggingrenderer.xml b/data/etc/car/android.car.cluster.loggingrenderer.xml
deleted file mode 100644
index 784e0e7..0000000
--- a/data/etc/car/android.car.cluster.loggingrenderer.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="android.car.cluster.loggingrenderer">
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/android.car.cluster.sample.xml b/data/etc/car/android.car.cluster.sample.xml
deleted file mode 100644
index 75c57b8..0000000
--- a/data/etc/car/android.car.cluster.sample.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="android.car.cluster.sample">
- <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/android.car.cluster.xml b/data/etc/car/android.car.cluster.xml
deleted file mode 100644
index de3acca..0000000
--- a/data/etc/car/android.car.cluster.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="android.car.cluster">
- <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- <permission name="android.car.permission.CAR_ENGINE_DETAILED"/>
- <permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/android.car.usb.handler.xml b/data/etc/car/android.car.usb.handler.xml
deleted file mode 100644
index c67847c..0000000
--- a/data/etc/car/android.car.usb.handler.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="android.car.usb.handler">
- <permission name="android.permission.MANAGE_USB"/>
- <permission name="android.permission.MANAGE_USERS"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.activityresolver.xml b/data/etc/car/com.android.car.activityresolver.xml
deleted file mode 100644
index 927c738..0000000
--- a/data/etc/car/com.android.car.activityresolver.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-<permissions>
- <privapp-permissions package="com.android.car.activityresolver">
- <permission name="android.permission.MANAGE_USERS"/>
- </privapp-permissions>
-</permissions>
-
diff --git a/data/etc/car/com.android.car.bugreport.xml b/data/etc/car/com.android.car.bugreport.xml
deleted file mode 100644
index ec9128e..0000000
--- a/data/etc/car/com.android.car.bugreport.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car.bugreport">
- <permission name="android.permission.DUMP"/>
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.READ_LOGS"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS"/>
- <permission name="android.car.permission.CAR_DRIVING_STATE"/>
- <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
- </privapp-permissions>
- </permissions>
diff --git a/data/etc/car/com.android.car.carlauncher.xml b/data/etc/car/com.android.car.carlauncher.xml
deleted file mode 100644
index 33f885a..0000000
--- a/data/etc/car/com.android.car.carlauncher.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car.carlauncher">
- <permission name="android.permission.ACTIVITY_EMBEDDING"/>
- <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
- <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
- <permission name="android.permission.PACKAGE_USAGE_STATS"/>
- <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.cluster.home.xml b/data/etc/car/com.android.car.cluster.home.xml
deleted file mode 100644
index a3d0fcf..0000000
--- a/data/etc/car/com.android.car.cluster.home.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car.cluster.home">
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/>
- <permission name="android.car.permission.CAR_MONITOR_INPUT"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.developeroptions.xml b/data/etc/car/com.android.car.developeroptions.xml
deleted file mode 100644
index c940574..0000000
--- a/data/etc/car/com.android.car.developeroptions.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car.developeroptions">
- <permission name="android.permission.ACCESS_CHECKIN_PROPERTIES"/>
- <permission name="android.permission.ACCESS_NOTIFICATIONS"/>
- <permission name="android.permission.BACKUP"/>
- <permission name="android.permission.BATTERY_STATS"/>
- <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
- <permission name="android.permission.CHANGE_APP_IDLE_STATE"/>
- <permission name="android.permission.CHANGE_CONFIGURATION"/>
- <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS"/>
- <permission name="android.permission.DELETE_PACKAGES"/>
- <permission name="android.permission.FORCE_STOP_PACKAGES"/>
- <permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
- <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
- <permission name="android.permission.MANAGE_DEBUGGING"/>
- <permission name="android.permission.MANAGE_FINGERPRINT"/>
- <permission name="android.permission.MANAGE_USB"/>
- <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.MASTER_CLEAR"/>
- <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
- <permission name="android.permission.MOVE_PACKAGE"/>
- <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
- <permission name="android.permission.PACKAGE_USAGE_STATS"/>
- <permission name="android.permission.READ_DREAM_STATE"/>
- <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
- <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
- <permission name="android.permission.REBOOT"/>
- <permission name="android.permission.REQUEST_NETWORK_SCORES"/>
- <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
- <permission name="android.permission.SET_TIME"/>
- <permission name="android.permission.STATUS_BAR"/>
- <permission name="android.permission.TETHER_PRIVILEGED"/>
- <permission name="android.permission.USE_RESERVED_DISK"/>
- <permission name="android.permission.USER_ACTIVITY"/>
- <permission name="android.permission.WRITE_APN_SETTINGS"/>
- <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
- <permission name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.dialer.xml b/data/etc/car/com.android.car.dialer.xml
deleted file mode 100644
index 97da67b..0000000
--- a/data/etc/car/com.android.car.dialer.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car.dialer">
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
- <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.hvac.xml b/data/etc/car/com.android.car.hvac.xml
deleted file mode 100644
index 534d44d..0000000
--- a/data/etc/car/com.android.car.hvac.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car.hvac">
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.media.xml b/data/etc/car/com.android.car.media.xml
deleted file mode 100644
index d17453d..0000000
--- a/data/etc/car/com.android.car.media.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car.media">
- <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.messenger.xml b/data/etc/car/com.android.car.messenger.xml
deleted file mode 100644
index 9e5f339..0000000
--- a/data/etc/car/com.android.car.messenger.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car.messenger">
- <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
- <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.notification.xml b/data/etc/car/com.android.car.notification.xml
deleted file mode 100644
index 8479512..0000000
--- a/data/etc/car/com.android.car.notification.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car.notification">
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.provision.xml b/data/etc/car/com.android.car.provision.xml
deleted file mode 100644
index 42cfd3c..0000000
--- a/data/etc/car/com.android.car.provision.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car.provision">
- <permission name="android.car.permission.CAR_POWERTRAIN"/>
- <permission name="android.permission.DISPATCH_PROVISIONING_MESSAGE"/>
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.MASTER_CLEAR"/>
- <permission name="android.permission.QUERY_ALL_PACKAGES"/>
- <permission name="android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"/>
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- <permission name="android.permission.WRITE_SETTINGS"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.radio.xml b/data/etc/car/com.android.car.radio.xml
deleted file mode 100644
index ed8652c..0000000
--- a/data/etc/car/com.android.car.radio.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car.radio">
- <permission name="android.permission.ACCESS_BROADCAST_RADIO"/>
- <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
- <permission name="android.car.permission.CAR_CONTROL_AUDIO_SETTINGS"/>
- <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.rotary.xml b/data/etc/car/com.android.car.rotary.xml
deleted file mode 100644
index a39b244..0000000
--- a/data/etc/car/com.android.car.rotary.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-<permissions>
- <privapp-permissions package="com.android.car.rotary">
- <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- <permission name="android.car.permission.CAR_MONITOR_INPUT"/>
- </privapp-permissions>
-</permissions>
-
diff --git a/data/etc/car/com.android.car.settings.xml b/data/etc/car/com.android.car.settings.xml
deleted file mode 100644
index 291ce80..0000000
--- a/data/etc/car/com.android.car.settings.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car.settings">
- <permission name="android.permission.ACCESS_CHECKIN_PROPERTIES"/>
- <permission name="android.permission.ACCESS_NOTIFICATIONS"/>
- <permission name="android.permission.BACKUP"/>
- <permission name="android.permission.BATTERY_STATS"/>
- <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
- <permission name="android.permission.CHANGE_APP_IDLE_STATE"/>
- <permission name="android.permission.CHANGE_CONFIGURATION"/>
- <permission name="android.permission.DELETE_PACKAGES"/>
- <permission name="android.permission.DELETE_CACHE_FILES"/>
- <permission name="android.permission.DUMP"/>
- <permission name="android.permission.FORCE_STOP_PACKAGES"/>
- <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
- <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
- <permission name="android.permission.MANAGE_DEBUGGING"/>
- <permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
- <permission name="android.permission.MANAGE_FINGERPRINT"/>
- <permission name="android.permission.MANAGE_USB"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.MANAGE_USER_OEM_UNLOCK_STATE" />
- <permission name="android.permission.MASTER_CLEAR"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
- <permission name="android.permission.MOVE_PACKAGE"/>
- <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
- <permission name="android.permission.PACKAGE_USAGE_STATS"/>
- <permission name="android.permission.READ_SEARCH_INDEXABLES"/>
- <permission name="android.permission.REBOOT"/>
- <permission name="android.permission.SET_TIME"/>
- <permission name="android.permission.SET_TIME_ZONE"/>
- <permission name="android.permission.STATUS_BAR"/>
- <permission name="android.permission.TETHER_PRIVILEGED"/>
- <permission name="android.permission.USE_RESERVED_DISK"/>
- <permission name="android.permission.USER_ACTIVITY"/>
- <permission name="android.permission.WRITE_APN_SETTINGS"/>
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.shell.xml b/data/etc/car/com.android.car.shell.xml
deleted file mode 100644
index c058cb9..0000000
--- a/data/etc/car/com.android.car.shell.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <!-- CarShell now overrides the shell package and adding permission here
- is ok. -->
- <privapp-permissions package="com.android.shell">
- <permission name="android.permission.INSTALL_PACKAGES" />
- <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
- <permission name="android.car.permission.CAR_CONTROL_AUDIO_SETTINGS"/>
- <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"/>
- <permission name="android.car.permission.CAR_DIAGNOSTICS"/>
- <permission name="android.car.permission.CAR_DRIVING_STATE"/>
- <permission name="android.car.permission.CAR_POWER"/>
- <permission name="android.car.permission.CONTROL_CAR_POWER_POLICY"/>
- <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
- <permission name="android.car.permission.CAR_TIRES"/>
- <permission name="android.car.permission.READ_CAR_STEERING"/>
- <permission name="android.car.permission.REQUEST_CAR_EVS_ACTIVITY" />
- <permission name="android.car.permission.CONTROL_CAR_EVS_ACTIVITY" />
- <permission name="android.car.permission.USE_CAR_EVS_CAMERA" />
- <permission name="android.car.permission.MONITOR_CAR_EVS_STATUS" />
- <permission name="android.car.permission.USE_CAR_WATCHDOG" />
- <permission name="android.car.permission.COLLECT_CAR_WATCHDOG_METRICS" />
- <permission name="android.car.permission.CONTROL_CAR_WATCHDOG_CONFIG" />
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.themeplayground.xml b/data/etc/car/com.android.car.themeplayground.xml
deleted file mode 100644
index cab4718..0000000
--- a/data/etc/car/com.android.car.themeplayground.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car.themeplayground">
- <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.ui.paintbooth.xml b/data/etc/car/com.android.car.ui.paintbooth.xml
deleted file mode 100644
index 11bf304..0000000
--- a/data/etc/car/com.android.car.ui.paintbooth.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
-
- <privapp-permissions package="com.android.car.ui.paintbooth">
- <!-- For enabling/disabling, and getting list of RROs -->
- <permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
- <!-- For showing the current active activity -->
- <permission name="android.permission.REAL_GET_TASKS"/>
- <!-- For getting list of RROs for current user -->
- <permission name="android.permission.MANAGE_USERS"/>
- <!-- For getting list of RROs for current user-->
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.car.xml b/data/etc/car/com.android.car.xml
deleted file mode 100644
index 48f6ab3..0000000
--- a/data/etc/car/com.android.car.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.car">
- <permission name="android.permission.LOCATION_HARDWARE"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
- <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- <permission name="android.permission.PROVIDE_TRUST_AGENT"/>
- <permission name="android.permission.REAL_GET_TASKS"/>
- <permission name="android.permission.REBOOT"/>
- <permission name="android.permission.READ_LOGS"/>
- <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.android.carsystemui.xml b/data/etc/car/com.android.carsystemui.xml
deleted file mode 100644
index a267d56..0000000
--- a/data/etc/car/com.android.carsystemui.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.android.systemui">
- <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
- <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"/>
- <permission name="android.car.permission.CAR_ENROLL_TRUST"/>
- <permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/>
- <permission name="android.car.permission.CAR_POWER"/>
- <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
- <permission name="android.car.permission.CONTROL_CAR_EVS_ACTIVITY"/>
- <permission name="android.car.permission.MONITOR_CAR_EVS_STATUS"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.google.android.car.adaslocation.xml b/data/etc/car/com.google.android.car.adaslocation.xml
deleted file mode 100644
index cc1ef3c..0000000
--- a/data/etc/car/com.google.android.car.adaslocation.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-<permissions>
- <privapp-permissions package="com.google.android.car.adaslocation">
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml
deleted file mode 100644
index 8705067..0000000
--- a/data/etc/car/com.google.android.car.kitchensink.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.google.android.car.kitchensink">
- <permission name="android.permission.ACCESS_NETWORK_STATE"/>
- <permission name="android.permission.ACCESS_WIFI_STATE"/>
- <permission name="android.permission.ACTIVITY_EMBEDDING"/>
- <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
- <permission name="android.permission.INJECT_EVENTS"/>
- <!-- use for CarServiceUnitTest and CarServiceTest -->
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <!-- use for CarServiceUnitTest -->
- <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
- <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
- <permission name="android.permission.LOCATION_HARDWARE"/>
- <permission name="android.permission.LOCK_DEVICE"/>
- <permission name="android.permission.MANAGE_USB"/>
- <permission name="android.permission.MANAGE_USERS"/>
- <permission name="android.permission.MASTER_CLEAR"/>
- <!-- use for CarServiceTest -->
- <permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
- <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
- <permission name="android.permission.MODIFY_DAY_NIGHT_MODE"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- <permission name="android.permission.MONITOR_INPUT"/>
- <permission name="android.permission.PROVIDE_TRUST_AGENT"/>
- <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
- <permission name="android.permission.REAL_GET_TASKS"/>
- <permission name="android.permission.READ_LOGS"/>
- <permission name="android.permission.REBOOT"/>
- <permission name="android.permission.RESET_PASSWORD"/>
- <permission name="android.permission.SEND_CATEGORY_CAR_NOTIFICATIONS"/>
- <!-- use for CarServiceTest -->
- <permission name="android.permission.SET_ACTIVITY_WATCHER"/>
- <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"/>
- <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
- <permission name="android.car.permission.CONTROL_APP_BLOCKING"/>
-
- <!-- use for rotary fragment to enable/disable packages related to rotary -->
- <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
-
- <!-- CarService permissions -->
- <!-- TODO: Explain why so many permissions are required -->
- <permission name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
- <permission name="android.car.permission.CAR_CONTROL_AUDIO_SETTINGS"/>
- <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME"/>
- <permission name="android.car.permission.CAR_DIAGNOSTICS"/>
- <permission name="android.car.permission.CAR_DISPLAY_IN_CLUSTER"/>
- <permission name="android.car.permission.CAR_DRIVING_STATE"/>
- <permission name="android.car.permission.CAR_DYNAMICS_STATE"/>
- <permission name="android.car.permission.CAR_EXTERIOR_LIGHTS"/>
- <permission name="android.car.permission.CAR_IDENTIFICATION"/>
- <permission name="android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL"/>
- <permission name="android.car.permission.CAR_MILEAGE"/>
- <permission name="android.car.permission.CAR_MOCK_VEHICLE_HAL"/>
- <permission name="android.car.permission.CAR_NAVIGATION_MANAGER"/>
- <permission name="android.car.permission.CAR_POWER"/>
- <permission name="android.car.permission.CAR_PROJECTION"/>
- <permission name="android.car.permission.CAR_TIRES"/>
- <permission name="android.car.permission.CAR_TEST_SERVICE"/>
- <permission name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION"/>
- <permission name="android.car.permission.CAR_VENDOR_EXTENSION"/>
- <permission name="android.car.permission.CONTROL_CAR_CLIMATE"/>
- <permission name="android.car.permission.CONTROL_CAR_DOORS"/>
- <permission name="android.car.permission.CONTROL_CAR_EXTERIOR_LIGHTS"/>
- <permission name="android.car.permission.CONTROL_CAR_FEATURES"/>
- <permission name="android.car.permission.CONTROL_CAR_MIRRORS"/>
- <permission name="android.car.permission.CONTROL_CAR_SEATS"/>
- <permission name="android.car.permission.CONTROL_CAR_WINDOWS"/>
- <permission name="android.car.permission.GET_CAR_VENDOR_CATEGORY_INFO"/>
- <permission name="android.car.permission.GET_CAR_VENDOR_CATEGORY_SEAT"/>
- <permission name="android.car.permission.READ_CAR_STEERING"/>
- <permission name="android.car.permission.SET_CAR_VENDOR_CATEGORY_INFO"/>
- <permission name="android.car.permission.STORAGE_MONITORING"/>
- <permission name="android.car.permission.VMS_PUBLISHER"/>
- <permission name="android.car.permission.VMS_SUBSCRIBER"/>
- <permission name="android.car.permission.REQUEST_CAR_EVS_ACTIVITY" />
- <permission name="android.car.permission.CONTROL_CAR_EVS_ACTIVITY" />
- <permission name="android.car.permission.USE_CAR_EVS_CAMERA" />
- <permission name="android.car.permission.MONITOR_CAR_EVS_STATUS" />
- <permission name="android.car.permission.USE_CAR_TELEMETRY_SERVICE" />
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/car/com.google.android.car.networking.preferenceupdater.xml b/data/etc/car/com.google.android.car.networking.preferenceupdater.xml
deleted file mode 100644
index cdeb8e4..0000000
--- a/data/etc/car/com.google.android.car.networking.preferenceupdater.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-<permissions>
- <privapp-permissions package="com.google.android.car.networking.preferenceupdater">
- <permission name="android.permission.ACCESS_NETWORK_STATE" />
- <permission name="android.permission.ACCESS_WIFI_STATE"/>
- <permission name="android.permission.ACTIVITY_EMBEDDING"/>
- <permission name="android.permission.CHANGE_NETWORK_STATE" />
- <permission name="android.permission.CONNECTIVITY_INTERNAL" />
- <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
- <permission name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERNCE" />
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- <permission name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
- <permission name="android.permission.INTERNET" />
- <permission name="android.permission.LOCATION_HARDWARE"/>
- <permission name="android.permission.PACKAGE_USAGE_STATS" />
- <permission name="android.permission.RECEIVE_BOOT_COMPLETED" />
- <permission name="android.permission.WAKE_LOCK" />
- <permission name="android.permission.WRITE_SETTINGS" />
- <permission name="android.car.permission.CAR_DRIVING_STATE" />
- </privapp-permissions>
-</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c250039..4c930d1 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -484,6 +484,7 @@
<permission name="android.permission.SCHEDULE_PRIORITIZED_ALARM" />
<!-- Permission required for CTS test - SystemMediaRouter2Test -->
<permission name="android.permission.MEDIA_CONTENT_CONTROL"/>
+ <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
<!-- Permission required for CTS test - CtsPermission5TestCases -->
<permission name="android.permission.RENOUNCE_PERMISSIONS" />
<permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 791aeb7..909ca39 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -3505,6 +3505,12 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "1805116444": {
+ "message": "We don't support remote animation for Task with multiple TaskFragmentOrganizers.",
+ "level": "ERROR",
+ "group": "WM_DEBUG_APP_TRANSITIONS",
+ "at": "com\/android\/server\/wm\/AppTransitionController.java"
+ },
"1810019902": {
"message": "TRANSIT_FLAG_OPEN_BEHIND, adding %s to mOpeningApps",
"level": "DEBUG",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index d326534..d925a92 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -233,14 +233,14 @@
+ " already exists");
}
mTaskListeners.put(listenerType, listener);
+ }
- // Notify the listener of all existing tasks with the given type.
- for (int i = mTasks.size() - 1; i >= 0; --i) {
- final TaskAppearedInfo data = mTasks.valueAt(i);
- final TaskListener taskListener = getTaskListener(data.getTaskInfo());
- if (taskListener != listener) continue;
- listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
- }
+ // Notify the listener of all existing tasks with the given type.
+ for (int i = mTasks.size() - 1; i >= 0; --i) {
+ final TaskAppearedInfo data = mTasks.valueAt(i);
+ final TaskListener taskListener = getTaskListener(data.getTaskInfo());
+ if (taskListener != listener) continue;
+ listener.onTaskAppeared(data.getTaskInfo(), data.getLeash());
}
}
}
@@ -266,8 +266,12 @@
tasks.add(data);
}
- // Remove listener
- mTaskListeners.removeAt(index);
+ // Remove listener, there can be the multiple occurrences, so search the whole list.
+ for (int i = mTaskListeners.size() - 1; i >= 0; --i) {
+ if (mTaskListeners.valueAt(i) == listener) {
+ mTaskListeners.removeAt(i);
+ }
+ }
// Associate tasks with new listeners if needed.
for (int i = tasks.size() - 1; i >= 0; --i) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
index 3ab0624..38079af 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java
@@ -537,6 +537,7 @@
mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled(
mContext.getContentResolver(), mUserId);
setSwipeToNotificationEnabled(enabled);
+ notifyShortcutStateChanged(mState.getState());
mOneHandedUiEventLogger.writeEvent(enabled
? OneHandedUiEventLogger.EVENT_ONE_HANDED_SETTINGS_SHOW_NOTIFICATION_ENABLED_ON
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 493870d..a046c42 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -250,7 +250,8 @@
setSideStagePosition(sideStagePosition, wct);
mMainStage.activate(getMainStageBounds(), wct);
mSideStage.addTask(task, getSideStageBounds(), wct);
- mTaskOrganizer.applyTransaction(wct);
+ mSyncQueue.queue(wct);
+ mSyncQueue.runInSync(t -> updateSurfaceBounds(null /* layout */, t));
return true;
}
@@ -446,15 +447,15 @@
setSideStagePosition(sideStagePosition, true /* updateBounds */, wct);
}
- private void setSideStagePosition(@SplitPosition int sideStagePosition,
- boolean updateBounds, @Nullable WindowContainerTransaction wct) {
+ private void setSideStagePosition(@SplitPosition int sideStagePosition, boolean updateBounds,
+ @Nullable WindowContainerTransaction wct) {
if (mSideStagePosition == sideStagePosition) return;
mSideStagePosition = sideStagePosition;
sendOnStagePositionChanged();
if (mSideStageListener.mVisible && updateBounds) {
if (wct == null) {
- // onBoundsChanged builds/applies a wct with the contents of updateWindowBounds.
+ // onLayoutChanged builds/applies a wct with the contents of updateWindowBounds.
onLayoutChanged(mSplitLayout);
} else {
updateWindowBounds(mSplitLayout, wct);
@@ -675,29 +676,11 @@
}
mSyncQueue.runInSync(t -> {
- final SurfaceControl sideStageLeash = mSideStage.mRootLeash;
- final SurfaceControl mainStageLeash = mMainStage.mRootLeash;
-
- if (sideStageVisible) {
- final Rect sideStageBounds = getSideStageBounds();
- t.setPosition(sideStageLeash,
- sideStageBounds.left, sideStageBounds.top)
- .setWindowCrop(sideStageLeash,
- sideStageBounds.width(), sideStageBounds.height());
- }
-
- if (mainStageVisible) {
- final Rect mainStageBounds = getMainStageBounds();
- t.setPosition(mainStageLeash, mainStageBounds.left, mainStageBounds.top)
- .setWindowCrop(mainStageLeash,
- mainStageBounds.width(), mainStageBounds.height());
- }
-
// Same above, we only set root tasks and divider leash visibility when both stage
// change to visible or invisible to avoid flicker.
if (sameVisibility) {
- t.setVisibility(sideStageLeash, bothStageVisible)
- .setVisibility(mainStageLeash, bothStageVisible);
+ t.setVisibility(mSideStage.mRootLeash, bothStageVisible)
+ .setVisibility(mMainStage.mRootLeash, bothStageVisible);
applyDividerVisibility(t);
}
});
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
index 6b74b62..d5acbbcf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -197,6 +197,43 @@
}
@Test
+ public void testAddListenerForMultipleTypes() {
+ RunningTaskInfo taskInfo1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN);
+ mOrganizer.onTaskAppeared(taskInfo1, null);
+ RunningTaskInfo taskInfo2 = createTaskInfo(2, WINDOWING_MODE_MULTI_WINDOW);
+ mOrganizer.onTaskAppeared(taskInfo2, null);
+
+ TrackingTaskListener listener = new TrackingTaskListener();
+ mOrganizer.addListenerForType(listener,
+ TASK_LISTENER_TYPE_MULTI_WINDOW, TASK_LISTENER_TYPE_FULLSCREEN);
+
+ // onTaskAppeared event should be delivered once for each taskInfo.
+ assertTrue(listener.appeared.contains(taskInfo1));
+ assertTrue(listener.appeared.contains(taskInfo2));
+ assertEquals(2, listener.appeared.size());
+ }
+
+ @Test
+ public void testRemoveListenerForMultipleTypes() {
+ RunningTaskInfo taskInfo1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN);
+ mOrganizer.onTaskAppeared(taskInfo1, null);
+ RunningTaskInfo taskInfo2 = createTaskInfo(2, WINDOWING_MODE_MULTI_WINDOW);
+ mOrganizer.onTaskAppeared(taskInfo2, null);
+
+ TrackingTaskListener listener = new TrackingTaskListener();
+ mOrganizer.addListenerForType(listener,
+ TASK_LISTENER_TYPE_MULTI_WINDOW, TASK_LISTENER_TYPE_FULLSCREEN);
+
+ mOrganizer.removeListener(listener);
+
+ // If listener is removed properly, onTaskInfoChanged event shouldn't be delivered.
+ mOrganizer.onTaskInfoChanged(taskInfo1);
+ assertTrue(listener.infoChanged.isEmpty());
+ mOrganizer.onTaskInfoChanged(taskInfo2);
+ assertTrue(listener.infoChanged.isEmpty());
+ }
+
+ @Test
public void testWindowingModeChange() {
RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW);
TrackingTaskListener mwListener = new TrackingTaskListener();
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index 4cce87a..2b685bf 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -32,6 +32,8 @@
using namespace android::uirenderer::renderthread;
+static constexpr bool sEnableExtraCropInset = true;
+
namespace android {
namespace uirenderer {
@@ -64,6 +66,20 @@
ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
return CopyResult::SourceEmpty;
}
+
+ if (sEnableExtraCropInset &&
+ (cropRect.right - cropRect.left != bitmap->width() ||
+ cropRect.bottom - cropRect.top != bitmap->height())) {
+ /*
+ * When we need use filtering, we should also make border shrink here like gui.
+ * But we could not check format for YUV or RGB here... Just use 1 pix.
+ */
+ cropRect.left += 0.5f;
+ cropRect.top += 0.5f;
+ cropRect.right -= 0.5f;
+ cropRect.bottom -= 0.5f;
+ }
+
UniqueAHardwareBuffer sourceBuffer{rawSourceBuffer};
AHardwareBuffer_Desc description;
AHardwareBuffer_describe(sourceBuffer.get(), &description);
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 2c81c97..48145d2 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -243,13 +243,12 @@
// the corresponding SkImageFilter each time.
// See b/193145089 and b/197263715
if (!Properties::enableRenderEffectCache) {
+ snapshotImage = renderNode->getLayerSurface()->makeImageSnapshot();
if (imageFilter) {
auto subset = SkIRect::MakeWH(srcBounds.width(), srcBounds.height());
snapshotImage = snapshotImage->makeWithFilter(recordingContext, imageFilter,
subset, clipBounds.roundOut(),
&srcBounds, &offset);
- } else {
- snapshotImage = renderNode->getLayerSurface()->makeImageSnapshot();
}
} else {
const auto snapshotResult = renderNode->updateSnapshotIfRequired(
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index f83c0a4..6755b7c 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -16,8 +16,21 @@
#include "RenderThread.h"
+#include <GrContextOptions.h>
+#include <android-base/properties.h>
+#include <dlfcn.h>
+#include <gl/GrGLInterface.h>
#include <gui/TraceUtils.h>
+#include <sys/resource.h>
+#include <ui/FatVector.h>
+#include <utils/Condition.h>
+#include <utils/Log.h>
+#include <utils/Mutex.h>
+
+#include <thread>
+
#include "../HardwareBitmapUploader.h"
+#include "CacheManager.h"
#include "CanvasContext.h"
#include "DeviceInfo.h"
#include "EglManager.h"
@@ -31,19 +44,6 @@
#include "renderstate/RenderState.h"
#include "utils/TimeUtils.h"
-#include <GrContextOptions.h>
-#include <gl/GrGLInterface.h>
-
-#include <dlfcn.h>
-#include <sys/resource.h>
-#include <utils/Condition.h>
-#include <utils/Log.h>
-#include <utils/Mutex.h>
-#include <thread>
-
-#include <android-base/properties.h>
-#include <ui/FatVector.h>
-
namespace android {
namespace uirenderer {
namespace renderthread {
@@ -112,18 +112,31 @@
"Failed to find required symbol ASurfaceTransaction_setZOrder!");
}
-void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) {
+void RenderThread::extendedFrameCallback(const AChoreographerFrameCallbackData* cbData,
+ void* data) {
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
- int64_t vsyncId = AChoreographer_getVsyncId(rt->mChoreographer);
- int64_t frameDeadline = AChoreographer_getFrameDeadline(rt->mChoreographer);
+ size_t preferredFrameTimelineIndex =
+ AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(cbData);
+ int64_t vsyncId = AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+ cbData, preferredFrameTimelineIndex);
+ int64_t frameDeadline = AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+ cbData, preferredFrameTimelineIndex);
+ int64_t frameTimeNanos = AChoreographerFrameCallbackData_getFrameTimeNanos(cbData);
+ // TODO(b/193273294): Remove when shared memory in use w/ expected present time always current.
int64_t frameInterval = AChoreographer_getFrameInterval(rt->mChoreographer);
- rt->mVsyncRequested = false;
- if (rt->timeLord().vsyncReceived(frameTimeNanos, frameTimeNanos, vsyncId, frameDeadline,
- frameInterval) && !rt->mFrameCallbackTaskPending) {
+ rt->frameCallback(vsyncId, frameDeadline, frameTimeNanos, frameInterval);
+}
+
+void RenderThread::frameCallback(int64_t vsyncId, int64_t frameDeadline, int64_t frameTimeNanos,
+ int64_t frameInterval) {
+ mVsyncRequested = false;
+ if (timeLord().vsyncReceived(frameTimeNanos, frameTimeNanos, vsyncId, frameDeadline,
+ frameInterval) &&
+ !mFrameCallbackTaskPending) {
ATRACE_NAME("queue mFrameCallbackTask");
- rt->mFrameCallbackTaskPending = true;
- nsecs_t runAt = (frameTimeNanos + rt->mDispatchFrameDelay);
- rt->queue().postAt(runAt, [=]() { rt->dispatchFrameCallbacks(); });
+ mFrameCallbackTaskPending = true;
+ nsecs_t runAt = (frameTimeNanos + mDispatchFrameDelay);
+ queue().postAt(runAt, [=]() { dispatchFrameCallbacks(); });
}
}
@@ -139,8 +152,8 @@
ChoreographerSource(RenderThread* renderThread) : mRenderThread(renderThread) {}
virtual void requestNextVsync() override {
- AChoreographer_postFrameCallback64(mRenderThread->mChoreographer,
- RenderThread::frameCallback, mRenderThread);
+ AChoreographer_postExtendedFrameCallback(
+ mRenderThread->mChoreographer, RenderThread::extendedFrameCallback, mRenderThread);
}
virtual void drainPendingEvents() override {
@@ -157,12 +170,16 @@
virtual void requestNextVsync() override {
mRenderThread->queue().postDelayed(16_ms, [this]() {
- RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread);
+ mRenderThread->frameCallback(UiFrameInfoBuilder::INVALID_VSYNC_ID,
+ std::numeric_limits<int64_t>::max(),
+ systemTime(SYSTEM_TIME_MONOTONIC), 16_ms);
});
}
virtual void drainPendingEvents() override {
- RenderThread::frameCallback(systemTime(SYSTEM_TIME_MONOTONIC), mRenderThread);
+ mRenderThread->frameCallback(UiFrameInfoBuilder::INVALID_VSYNC_ID,
+ std::numeric_limits<int64_t>::max(),
+ systemTime(SYSTEM_TIME_MONOTONIC), 16_ms);
}
private:
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 0b81fc04..c1f6790 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -211,7 +211,9 @@
// corresponding callbacks for each display event type
static int choreographerCallback(int fd, int events, void* data);
// Callback that will be run on vsync ticks.
- static void frameCallback(int64_t frameTimeNanos, void* data);
+ static void extendedFrameCallback(const AChoreographerFrameCallbackData* cbData, void* data);
+ void frameCallback(int64_t vsyncId, int64_t frameDeadline, int64_t frameTimeNanos,
+ int64_t frameInterval);
// Callback that will be run whenver there is a refresh rate change.
static void refreshRateCallback(int64_t vsyncPeriod, void* data);
void drainDisplayEventQueue();
diff --git a/location/java/android/location/GpsNavigationMessage.java b/location/java/android/location/GpsNavigationMessage.java
index dc1e99f..2b978f7 100644
--- a/location/java/android/location/GpsNavigationMessage.java
+++ b/location/java/android/location/GpsNavigationMessage.java
@@ -262,12 +262,8 @@
parcel.readByteArray(data);
navigationMessage.setData(data);
- if (parcel.dataAvail() >= Integer.SIZE) {
- int status = parcel.readInt();
- navigationMessage.setStatus((short) status);
- } else {
- navigationMessage.setStatus(STATUS_UNKNOWN);
- }
+ int status = parcel.readInt();
+ navigationMessage.setStatus((short) status);
return navigationMessage;
}
diff --git a/media/Android.bp b/media/Android.bp
index aab1647..aa07c13 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -65,15 +65,27 @@
"aidl/android/media/audio/common/AudioEncapsulationType.aidl",
"aidl/android/media/audio/common/AudioFormatDescription.aidl",
"aidl/android/media/audio/common/AudioFormatType.aidl",
+ "aidl/android/media/audio/common/AudioGain.aidl",
+ "aidl/android/media/audio/common/AudioGainConfig.aidl",
+ "aidl/android/media/audio/common/AudioGainMode.aidl",
"aidl/android/media/audio/common/AudioMMapPolicy.aidl",
"aidl/android/media/audio/common/AudioMMapPolicyInfo.aidl",
"aidl/android/media/audio/common/AudioMMapPolicyType.aidl",
"aidl/android/media/audio/common/AudioMode.aidl",
"aidl/android/media/audio/common/AudioOffloadInfo.aidl",
+ "aidl/android/media/audio/common/AudioPort.aidl",
+ "aidl/android/media/audio/common/AudioPortConfig.aidl",
+ "aidl/android/media/audio/common/AudioPortExt.aidl",
+ "aidl/android/media/audio/common/AudioPortMixExt.aidl",
+ "aidl/android/media/audio/common/AudioPortMixExtUseCase.aidl",
+ "aidl/android/media/audio/common/AudioProfile.aidl",
"aidl/android/media/audio/common/AudioSource.aidl",
+ "aidl/android/media/audio/common/AudioStandard.aidl",
"aidl/android/media/audio/common/AudioStreamType.aidl",
"aidl/android/media/audio/common/AudioUsage.aidl",
"aidl/android/media/audio/common/AudioUuid.aidl",
+ "aidl/android/media/audio/common/ExtraAudioDescriptor.aidl",
+ "aidl/android/media/audio/common/Int.aidl",
"aidl/android/media/audio/common/PcmType.aidl",
],
stability: "vintf",
diff --git a/media/aidl/android/media/audio/common/AudioGain.aidl b/media/aidl/android/media/audio/common/AudioGain.aidl
new file mode 100644
index 0000000..90fdeb6
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioGain.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.media.audio.common;
+
+import android.media.audio.common.AudioChannelLayout;
+
+/**
+ * This structure represents a gain stage. A gain stage is always attached
+ * to an AudioPort.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioGain {
+ /** Bitmask, indexed by AudioGainMode. */
+ int mode;
+ /** For AudioGainMode.CHANNELS, specifies controlled channels. */
+ AudioChannelLayout channelMask;
+ /** Minimum gain value in millibels. */
+ int minValue;
+ /** Maximum gain value in millibels. */
+ int maxValue;
+ /** Default gain value in millibels. */
+ int defaultValue;
+ /** Gain step in millibels. */
+ int stepValue;
+ /** Minimum ramp duration in milliseconds. */
+ int minRampMs;
+ /** Maximum ramp duration in milliseconds. */
+ int maxRampMs;
+}
diff --git a/media/aidl/android/media/audio/common/AudioGainConfig.aidl b/media/aidl/android/media/audio/common/AudioGainConfig.aidl
new file mode 100644
index 0000000..a247111
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioGainConfig.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.media.audio.common;
+
+import android.media.audio.common.AudioChannelLayout;
+
+/**
+ * The gain configuration structure is used to get or set the gain values of a
+ * given AudioPort.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioGainConfig {
+ /** Index of the corresponding AudioGain in AudioPort.gains. */
+ int index;
+ /** Bitmask, indexed by AudioGainMode. */
+ int mode;
+ /** For AudioGainMode.CHANNELS, specifies controlled channels. */
+ AudioChannelLayout channelMask;
+ /**
+ * Gain values in millibels. For each channel ordered from LSb to MSb in
+ * channel mask. The number of values is 1 in joint mode, otherwise equals
+ * the number of bits implied by channelMask.
+ */
+ int[] values;
+ /** Ramp duration in milliseconds. */
+ int rampDurationMs;
+}
diff --git a/media/aidl/android/media/audio/common/AudioGainMode.aidl b/media/aidl/android/media/audio/common/AudioGainMode.aidl
new file mode 100644
index 0000000..e46752e
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioGainMode.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.media.audio.common;
+
+/**
+ * Type of gain control exposed by an audio port. The values are
+ * indexes of bits in a bitmask.
+ *
+ * {@hide}
+ */
+@VintfStability
+@Backing(type="byte")
+enum AudioGainMode {
+ /** Gain is the same for all channels. */
+ JOINT = 0,
+ /** The gain is set individually for each channel. */
+ CHANNELS = 1,
+ /** Ramping is applied. */
+ RAMP = 2,
+}
diff --git a/media/aidl/android/media/audio/common/AudioMMapPolicy.aidl b/media/aidl/android/media/audio/common/AudioMMapPolicy.aidl
index bbb0402..e7e8710d 100644
--- a/media/aidl/android/media/audio/common/AudioMMapPolicy.aidl
+++ b/media/aidl/android/media/audio/common/AudioMMapPolicy.aidl
@@ -29,13 +29,13 @@
/**
* The MMAP feature is disabled and never used.
*/
- NEVER = 1,
+ NEVER = 1,
/**
* If MMAP feature works then uses it. Otherwise, fall back to something else.
*/
- AUTO = 2,
+ AUTO = 2,
/**
* The MMAP feature must be used. If not available then fail.
*/
- ALWAYS = 3,
+ ALWAYS = 3,
}
diff --git a/media/aidl/android/media/audio/common/AudioMMapPolicyType.aidl b/media/aidl/android/media/audio/common/AudioMMapPolicyType.aidl
index bbc6e57..9a15b56 100644
--- a/media/aidl/android/media/audio/common/AudioMMapPolicyType.aidl
+++ b/media/aidl/android/media/audio/common/AudioMMapPolicyType.aidl
@@ -26,10 +26,10 @@
* Default aaudio mmap policy. It is used to query whether the
* aaudio MMAP could be used or not.
*/
- DEFAULT = 1,
+ DEFAULT = 1,
/**
* Exclusive aaudio mmap policy. It is used to query whether the
* aaudio MMAP could be used in exclusive mode or not.
*/
- EXCLUSIVE = 2,
+ EXCLUSIVE = 2,
}
diff --git a/media/aidl/android/media/audio/common/AudioPort.aidl b/media/aidl/android/media/audio/common/AudioPort.aidl
new file mode 100644
index 0000000..19652bc
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioPort.aidl
@@ -0,0 +1,59 @@
+/*
+ * 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.media.audio.common;
+
+import android.media.audio.common.AudioGain;
+import android.media.audio.common.AudioPortConfig;
+import android.media.audio.common.AudioPortExt;
+import android.media.audio.common.AudioProfile;
+import android.media.audio.common.ExtraAudioDescriptor;
+
+/**
+ * Audio port structure describes the capabilities of an audio port
+ * as well as its current configuration.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioPort {
+ /**
+ * Unique identifier of the port within this HAL service.
+ */
+ int id;
+ /**
+ * Human-readable name describing the function of the port.
+ * E.g. "telephony_tx" or "fm_tuner".
+ */
+ @utf8InCpp String name;
+ /**
+ * AudioProfiles supported by this port: format, rates, channels.
+ */
+ AudioProfile[] profiles;
+ /**
+ * ExtraAudioDescriptors supported by this port. Used for formats not
+ * recognized by the platform. The audio capability is described by a
+ * hardware descriptor.
+ */
+ ExtraAudioDescriptor[] extraAudioDescriptors;
+ /** Gain controllers. */
+ AudioGain[] gains;
+ /** Current audio port configuration. */
+ AudioPortConfig activeConfig;
+ /** Extra parameters depending on the port role. */
+ AudioPortExt ext;
+}
diff --git a/media/aidl/android/media/audio/common/AudioPortConfig.aidl b/media/aidl/android/media/audio/common/AudioPortConfig.aidl
new file mode 100644
index 0000000..44850f4
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioPortConfig.aidl
@@ -0,0 +1,49 @@
+/*
+ * 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.media.audio.common;
+
+import android.media.audio.common.AudioChannelLayout;
+import android.media.audio.common.AudioFormatDescription;
+import android.media.audio.common.AudioGainConfig;
+import android.media.audio.common.AudioPortExt;
+import android.media.audio.common.Int;
+
+/**
+ * Audio port configuration structure specifies a particular configuration
+ * of an audio port.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioPortConfig {
+ /**
+ * Port unique ID. This field is set to a non-zero value when it is needed
+ * to select a previously reported port and apply new configuration to it.
+ */
+ int id;
+ /** Sample rate in Hz. Can be left unspecified. */
+ @nullable Int sampleRate;
+ /** Channel mask. Can be left unspecified. */
+ @nullable AudioChannelLayout channelMask;
+ /** Format. Can be left unspecified. */
+ @nullable AudioFormatDescription format;
+ /** Gain to apply. Can be left unspecified. */
+ @nullable AudioGainConfig gain;
+ /** Extra parameters depending on the port role. */
+ AudioPortExt ext;
+}
diff --git a/media/aidl/android/media/audio/common/AudioPortExt.aidl b/media/aidl/android/media/audio/common/AudioPortExt.aidl
new file mode 100644
index 0000000..9b60c57
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioPortExt.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.media.audio.common;
+
+import android.media.audio.common.AudioDevice;
+import android.media.audio.common.AudioPortMixExt;
+
+/**
+ * Extra parameters of an AudioPort/AudioPortConfig that depend on
+ * the actual port role.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+union AudioPortExt {
+ /** Represents an empty union. Value is ignored. */
+ boolean unspecified;
+ /** Audio device specification. */
+ AudioDevice device;
+ /** Mix specific info. */
+ AudioPortMixExt mix;
+ /** Audio session identifier. */
+ int session;
+}
diff --git a/media/aidl/android/media/audio/common/AudioPortMixExt.aidl b/media/aidl/android/media/audio/common/AudioPortMixExt.aidl
new file mode 100644
index 0000000..54c5d8b
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioPortMixExt.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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.media.audio.common;
+
+import android.media.audio.common.AudioPortMixExtUseCase;
+
+/**
+ * Extra parameters which are specified when the audio port is in the mix role.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioPortMixExt {
+ /** I/O handle of the input/output stream. */
+ int handle;
+ /** Parameters specific to the mix use case. */
+ AudioPortMixExtUseCase usecase;
+}
diff --git a/media/aidl/android/media/audio/common/AudioPortMixExtUseCase.aidl b/media/aidl/android/media/audio/common/AudioPortMixExtUseCase.aidl
new file mode 100644
index 0000000..af9ada0
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioPortMixExtUseCase.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.media.audio.common;
+
+import android.media.audio.common.AudioSource;
+import android.media.audio.common.AudioStreamType;
+
+/**
+ * Provides additional information depending on the type of the audio port
+ * when it is used in the mix role.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+union AudioPortMixExtUseCase {
+ /**
+ * This is the default case for this union. The value is ignored.
+ */
+ boolean unspecified;
+ /**
+ * This case applies when the audio port is a source.
+ * The value specifies the destination stream type.
+ */
+ AudioStreamType stream;
+ /**
+ * This case applies when the audio port is a sink.
+ * The value specifies the source.
+ */
+ AudioSource source;
+}
diff --git a/media/aidl/android/media/audio/common/AudioProfile.aidl b/media/aidl/android/media/audio/common/AudioProfile.aidl
new file mode 100644
index 0000000..2124b0d
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioProfile.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.media.audio.common;
+
+import android.media.audio.common.AudioChannelLayout;
+import android.media.audio.common.AudioEncapsulationType;
+import android.media.audio.common.AudioFormatDescription;
+
+/**
+ * AudioProfile describes a set of configurations supported for a certain
+ * audio format. A profile can be either "static" which means all the
+ * configurations are predefined, or "dynamic" which means configurations
+ * are queried at run time. Dynamic profiles generally used with detachable
+ * devices, e.g. HDMI or USB devices.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioProfile {
+ /** Name is commonly used with static profiles. Can be empty. */
+ @utf8InCpp String name;
+ /** If the format is set to 'DEFAULT', this indicates a dynamic profile. */
+ AudioFormatDescription format;
+ /** Can be empty if channel masks are "dynamic". */
+ AudioChannelLayout[] channelMasks;
+ /** Can be empty if sample rates are "dynamic". */
+ int[] sampleRates;
+ /** For encoded audio formats, an encapsulation can be specified. */
+ AudioEncapsulationType encapsulationType = AudioEncapsulationType.NONE;
+}
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/package-info.java b/media/aidl/android/media/audio/common/AudioStandard.aidl
similarity index 67%
copy from apex/appsearch/testing/java/com/android/server/appsearch/testing/package-info.java
copy to media/aidl/android/media/audio/common/AudioStandard.aidl
index 5c919b4..0529e91 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/package-info.java
+++ b/media/aidl/android/media/audio/common/AudioStandard.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -13,8 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.media.audio.common;
/**
- * @hide
+ * The audio standard that describe audio playback/capture capabilites.
+ *
+ * {@hide}
*/
-package com.android.server.appsearch.testing;
+@Backing(type="int")
+@VintfStability
+enum AudioStandard {
+ NONE = 0,
+ EDID = 1,
+}
diff --git a/media/aidl/android/media/audio/common/ExtraAudioDescriptor.aidl b/media/aidl/android/media/audio/common/ExtraAudioDescriptor.aidl
new file mode 100644
index 0000000..330e0d1
--- /dev/null
+++ b/media/aidl/android/media/audio/common/ExtraAudioDescriptor.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.media.audio.common;
+
+import android.media.audio.common.AudioEncapsulationType;
+import android.media.audio.common.AudioStandard;
+
+/**
+ * The audio descriptor that descibes playback/capture capabilities according to
+ * a particular standard.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable ExtraAudioDescriptor {
+ AudioStandard standard = AudioStandard.NONE;
+ byte[] audioDescriptor;
+ AudioEncapsulationType encapsulationType = AudioEncapsulationType.NONE;
+}
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/package-info.java b/media/aidl/android/media/audio/common/Int.aidl
similarity index 62%
copy from apex/appsearch/testing/java/com/android/server/appsearch/testing/package-info.java
copy to media/aidl/android/media/audio/common/Int.aidl
index 5c919b4..01c768e 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/package-info.java
+++ b/media/aidl/android/media/audio/common/Int.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -14,7 +14,16 @@
* limitations under the License.
*/
+package android.media.audio.common;
+
/**
- * @hide
+ * This is a simple wrapper around an 'int', putting it in a parcelable, so it
+ * can be used as an 'inout' parameter, be made '@nullable', etc.
+ *
+ * {@hide}
*/
-package com.android.server.appsearch.testing;
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable Int {
+ int value;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioGain.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioGain.aidl
new file mode 100644
index 0000000..71891eb
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioGain.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioGain {
+ int mode;
+ android.media.audio.common.AudioChannelLayout channelMask;
+ int minValue;
+ int maxValue;
+ int defaultValue;
+ int stepValue;
+ int minRampMs;
+ int maxRampMs;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioGainConfig.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioGainConfig.aidl
new file mode 100644
index 0000000..01877c7
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioGainConfig.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioGainConfig {
+ int index;
+ int mode;
+ android.media.audio.common.AudioChannelLayout channelMask;
+ int[] values;
+ int rampDurationMs;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioGainMode.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioGainMode.aidl
new file mode 100644
index 0000000..fddc20c
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioGainMode.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="byte") @VintfStability
+enum AudioGainMode {
+ JOINT = 0,
+ CHANNELS = 1,
+ RAMP = 2,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPort.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPort.aidl
new file mode 100644
index 0000000..ceb3774
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPort.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioPort {
+ int id;
+ @utf8InCpp String name;
+ android.media.audio.common.AudioProfile[] profiles;
+ android.media.audio.common.ExtraAudioDescriptor[] extraAudioDescriptors;
+ android.media.audio.common.AudioGain[] gains;
+ android.media.audio.common.AudioPortConfig activeConfig;
+ android.media.audio.common.AudioPortExt ext;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortConfig.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortConfig.aidl
new file mode 100644
index 0000000..fcc5458
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortConfig.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioPortConfig {
+ int id;
+ @nullable android.media.audio.common.Int sampleRate;
+ @nullable android.media.audio.common.AudioChannelLayout channelMask;
+ @nullable android.media.audio.common.AudioFormatDescription format;
+ @nullable android.media.audio.common.AudioGainConfig gain;
+ android.media.audio.common.AudioPortExt ext;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortExt.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortExt.aidl
new file mode 100644
index 0000000..241e4e6
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortExt.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+union AudioPortExt {
+ boolean unspecified;
+ android.media.audio.common.AudioDevice device;
+ android.media.audio.common.AudioPortMixExt mix;
+ int session;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortMixExt.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortMixExt.aidl
new file mode 100644
index 0000000..547396c
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortMixExt.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioPortMixExt {
+ int handle;
+ android.media.audio.common.AudioPortMixExtUseCase usecase;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortMixExtUseCase.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortMixExtUseCase.aidl
new file mode 100644
index 0000000..e9acb40
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioPortMixExtUseCase.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+union AudioPortMixExtUseCase {
+ boolean unspecified;
+ android.media.audio.common.AudioStreamType stream;
+ android.media.audio.common.AudioSource source;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioProfile.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioProfile.aidl
new file mode 100644
index 0000000..134cdd9
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioProfile.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioProfile {
+ @utf8InCpp String name;
+ android.media.audio.common.AudioFormatDescription format;
+ android.media.audio.common.AudioChannelLayout[] channelMasks;
+ int[] sampleRates;
+ android.media.audio.common.AudioEncapsulationType encapsulationType = android.media.audio.common.AudioEncapsulationType.NONE;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStandard.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStandard.aidl
new file mode 100644
index 0000000..6c4490f
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStandard.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioStandard {
+ NONE = 0,
+ EDID = 1,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/ExtraAudioDescriptor.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/ExtraAudioDescriptor.aidl
new file mode 100644
index 0000000..2ae2405
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/ExtraAudioDescriptor.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable ExtraAudioDescriptor {
+ android.media.audio.common.AudioStandard standard = android.media.audio.common.AudioStandard.NONE;
+ byte[] audioDescriptor;
+ android.media.audio.common.AudioEncapsulationType encapsulationType = android.media.audio.common.AudioEncapsulationType.NONE;
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/Int.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/Int.aidl
new file mode 100644
index 0000000..b0d3c49
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/Int.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable Int {
+ int value;
+}
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 48289ec..b59c71f 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -69,7 +69,9 @@
void releaseSessionWithRouter2(IMediaRouter2 router, String sessionId);
// Methods for MediaRouter2Manager
- List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager);
+ List<RoutingSessionInfo> getRemoteSessions(IMediaRouter2Manager manager);
+ RoutingSessionInfo getSystemSessionInfoForPackage(
+ IMediaRouter2Manager manager, String packageName);
void registerManager(IMediaRouter2Manager manager, String packageName);
void unregisterManager(IMediaRouter2Manager manager);
void setRouteVolumeWithManager(IMediaRouter2Manager manager, int requestId,
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 62b9cb0..e432eb6 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -48,6 +48,7 @@
import android.view.Display;
import android.view.DisplayAddress;
+import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
@@ -148,7 +149,7 @@
ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
mSystemCategory = new RouteCategory(
- com.android.internal.R.string.default_audio_route_category_name,
+ R.string.default_audio_route_category_name,
ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO, false);
mSystemCategory.mIsSystem = true;
@@ -163,14 +164,15 @@
// Called after sStatic is initialized
void startMonitoringRoutes(Context appContext) {
mDefaultAudioVideo = new RouteInfo(mSystemCategory);
- mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;
+ mDefaultAudioVideo.mNameResId = R.string.default_audio_route_name;
mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
mDefaultAudioVideo.updatePresentationDisplay();
if (((AudioManager) appContext.getSystemService(Context.AUDIO_SERVICE))
.isVolumeFixed()) {
mDefaultAudioVideo.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED;
}
-
+ mDefaultAudioVideo.mGlobalRouteId = sStatic.mResources.getString(
+ R.string.default_audio_route_id);
addRouteStatic(mDefaultAudioVideo);
// This will select the active wifi display route if there is one.
@@ -215,15 +217,15 @@
int name;
if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0
|| (newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
- name = com.android.internal.R.string.default_audio_route_name_headphones;
+ name = R.string.default_audio_route_name_headphones;
} else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
- name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
+ name = R.string.default_audio_route_name_dock_speakers;
} else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HDMI) != 0) {
- name = com.android.internal.R.string.default_audio_route_name_hdmi;
+ name = R.string.default_audio_route_name_hdmi;
} else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_USB) != 0) {
- name = com.android.internal.R.string.default_audio_route_name_usb;
+ name = R.string.default_audio_route_name_usb;
} else {
- name = com.android.internal.R.string.default_audio_route_name;
+ name = R.string.default_audio_route_name;
}
mDefaultAudioVideo.mNameResId = name;
dispatchRouteChanged(mDefaultAudioVideo);
@@ -243,9 +245,12 @@
final RouteInfo info = new RouteInfo(mSystemCategory);
info.mName = newRoutes.bluetoothName;
info.mDescription = mResources.getText(
- com.android.internal.R.string.bluetooth_a2dp_audio_route_name);
+ R.string.bluetooth_a2dp_audio_route_name);
info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
info.mDeviceType = RouteInfo.DEVICE_TYPE_BLUETOOTH;
+ info.mGlobalRouteId = sStatic.mResources.getString(
+ R.string.bluetooth_a2dp_audio_route_id);
+
mBluetoothA2dpRoute = info;
addRouteStatic(mBluetoothA2dpRoute);
} else {
@@ -508,6 +513,9 @@
outer: for (int i = mRoutes.size(); i-- > 0; ) {
final RouteInfo route = mRoutes.get(i);
final String globalRouteId = route.mGlobalRouteId;
+ if (route.isDefault() || route.isBluetooth()) {
+ continue;
+ }
if (globalRouteId != null) {
for (int j = 0; j < globalRouteCount; j++) {
MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(j);
@@ -1572,7 +1580,7 @@
newRoute.mEnabled = isWifiDisplayEnabled(display, wfdStatus);
newRoute.mName = display.getFriendlyDisplayName();
newRoute.mDescription = sStatic.mResources.getText(
- com.android.internal.R.string.wireless_display_route_description);
+ R.string.wireless_display_route_description);
newRoute.updatePresentationDisplay();
newRoute.mDeviceType = RouteInfo.DEVICE_TYPE_TV;
return newRoute;
@@ -1867,19 +1875,19 @@
int resId;
switch (statusCode) {
case STATUS_SCANNING:
- resId = com.android.internal.R.string.media_route_status_scanning;
+ resId = R.string.media_route_status_scanning;
break;
case STATUS_CONNECTING:
- resId = com.android.internal.R.string.media_route_status_connecting;
+ resId = R.string.media_route_status_connecting;
break;
case STATUS_AVAILABLE:
- resId = com.android.internal.R.string.media_route_status_available;
+ resId = R.string.media_route_status_available;
break;
case STATUS_NOT_AVAILABLE:
- resId = com.android.internal.R.string.media_route_status_not_available;
+ resId = R.string.media_route_status_not_available;
break;
case STATUS_IN_USE:
- resId = com.android.internal.R.string.media_route_status_in_use;
+ resId = R.string.media_route_status_in_use;
break;
case STATUS_CONNECTED:
case STATUS_NONE:
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index fbf7def..4b32dbf 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -300,7 +300,8 @@
mManagerCallback = new ManagerCallback();
mHandler = new Handler(Looper.getMainLooper());
mSystemController = new SystemRoutingController(
- ensureClientPackageNameForSystemSession(sManager.getSystemRoutingSession()));
+ ensureClientPackageNameForSystemSession(
+ sManager.getSystemRoutingSession(clientPackageName)));
mDiscoveryPreference = new RouteDiscoveryPreference.Builder(
sManager.getPreferredFeatures(clientPackageName), true).build();
updateAllRoutesFromManager();
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 7f7fb60..83fa7c2 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -351,16 +351,21 @@
}
/**
- * Gets the system routing session associated with no specific application.
+ * Gets the system routing session for the given {@code packageName}.
+ * Apps can select a route that is not the global route. (e.g. an app can select the device
+ * route while BT route is available.)
+ *
+ * @param packageName the package name of the application.
*/
- @NonNull
- public RoutingSessionInfo getSystemRoutingSession() {
- for (RoutingSessionInfo sessionInfo : getActiveSessions()) {
- if (sessionInfo.isSystemSession()) {
- return sessionInfo;
- }
+ @Nullable
+ public RoutingSessionInfo getSystemRoutingSession(@Nullable String packageName) {
+ try {
+ return mMediaRouterService.getSystemSessionInfoForPackage(
+ getOrCreateClient(), packageName);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "Unable to get current system session info", ex);
}
- throw new IllegalStateException("No system routing session");
+ return null;
}
/**
@@ -377,13 +382,10 @@
return null;
}
if (playbackInfo.getPlaybackType() == MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
- return new RoutingSessionInfo.Builder(getSystemRoutingSession())
- .setClientPackageName(mediaController.getPackageName())
- .build();
+ return getSystemRoutingSession(mediaController.getPackageName());
}
- for (RoutingSessionInfo sessionInfo : getActiveSessions()) {
- if (!sessionInfo.isSystemSession()
- && areSessionsMatched(mediaController, sessionInfo)) {
+ for (RoutingSessionInfo sessionInfo : getRemoteSessions()) {
+ if (areSessionsMatched(mediaController, sessionInfo)) {
return sessionInfo;
}
}
@@ -395,20 +397,17 @@
* The first element of the returned list is the system routing session.
*
* @param packageName the package name of the application that is routing.
- * @see #getSystemRoutingSession()
+ * @see #getSystemRoutingSession(String)
*/
@NonNull
public List<RoutingSessionInfo> getRoutingSessions(@NonNull String packageName) {
Objects.requireNonNull(packageName, "packageName must not be null");
List<RoutingSessionInfo> sessions = new ArrayList<>();
+ sessions.add(getSystemRoutingSession(packageName));
- for (RoutingSessionInfo sessionInfo : getActiveSessions()) {
- if (sessionInfo.isSystemSession()) {
- sessions.add(new RoutingSessionInfo.Builder(sessionInfo)
- .setClientPackageName(packageName)
- .build());
- } else if (TextUtils.equals(sessionInfo.getClientPackageName(), packageName)) {
+ for (RoutingSessionInfo sessionInfo : getRemoteSessions()) {
+ if (TextUtils.equals(sessionInfo.getClientPackageName(), packageName)) {
sessions.add(sessionInfo);
}
}
@@ -416,23 +415,21 @@
}
/**
- * Gets the list of all active routing sessions.
+ * Gets the list of all routing sessions except the system routing session.
* <p>
- * The first element of the list is the system routing session containing
- * phone speakers, wired headset, Bluetooth devices.
- * The system routing session is shared by apps such that controlling it will affect
- * all apps.
* If you want to transfer media of an application, use {@link #getRoutingSessions(String)}.
+ * If you want to get only the system routing session, use
+ * {@link #getSystemRoutingSession(String)}.
*
* @see #getRoutingSessions(String)
- * @see #getSystemRoutingSession()
+ * @see #getSystemRoutingSession(String)
*/
@NonNull
- public List<RoutingSessionInfo> getActiveSessions() {
+ public List<RoutingSessionInfo> getRemoteSessions() {
Client client = getOrCreateClient();
if (client != null) {
try {
- return mMediaRouterService.getActiveSessions(client);
+ return mMediaRouterService.getRemoteSessions(client);
} catch (RemoteException ex) {
Log.e(TAG, "Unable to get sessions. Service probably died.", ex);
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 3a34e75..d7e9ae9 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -115,7 +115,8 @@
public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
- mUiAutomation.adoptShellPermissionIdentity(Manifest.permission.MEDIA_CONTENT_CONTROL);
+ mUiAutomation.adoptShellPermissionIdentity(Manifest.permission.MEDIA_CONTENT_CONTROL,
+ Manifest.permission.MODIFY_AUDIO_ROUTING);
MediaRouter2ManagerTestActivity.startActivity(mContext);
mManager = MediaRouter2Manager.getInstance(mContext);
@@ -305,7 +306,7 @@
mManager.selectRoute(mPackageName, routeToSelect);
assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- assertEquals(2, mManager.getActiveSessions().size());
+ assertEquals(1, mManager.getRemoteSessions().size());
}
@Test
@@ -368,7 +369,7 @@
.addFeature(FEATURE_REMOTE_PLAYBACK)
.build();
- mManager.transfer(mManager.getSystemRoutingSession(), unknownRoute);
+ mManager.transfer(mManager.getSystemRoutingSession(null), unknownRoute);
assertFalse(onSessionCreatedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
assertTrue(onTransferFailedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
@@ -512,12 +513,12 @@
assertFalse(managerOnSessionReleasedLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
assertEquals(2, sessions.size());
- List<String> activeSessionIds = mManager.getActiveSessions().stream()
+ List<String> remoteSessionIds = mManager.getRemoteSessions().stream()
.map(RoutingSessionInfo::getId)
.collect(Collectors.toList());
- // The old session shouldn't appear on the active session list.
- assertFalse(activeSessionIds.contains(sessions.get(0).getId()));
- assertTrue(activeSessionIds.contains(sessions.get(1).getId()));
+ // The old session shouldn't appear on the session list.
+ assertFalse(remoteSessionIds.contains(sessions.get(0).getId()));
+ assertTrue(remoteSessionIds.contains(sessions.get(1).getId()));
assertFalse(serviceOnReleaseSessionLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
mManager.releaseSession(sessions.get(0));
@@ -554,13 +555,14 @@
assertTrue(onFailedLatch.await(MediaRouter2Manager.TRANSFER_TIMEOUT_MS,
TimeUnit.MILLISECONDS));
}
+
@Test
public void testSetSystemRouteVolume() throws Exception {
// ensure client
addManagerCallback(new MediaRouter2Manager.Callback() {});
String selectedSystemRouteId =
MediaRouter2Utils.getOriginalId(
- mManager.getActiveSessions().get(0).getSelectedRoutes().get(0));
+ mManager.getSystemRoutingSession(mPackageName).getSelectedRoutes().get(0));
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
MediaRoute2Info volRoute = routes.get(selectedSystemRouteId);
assertNotNull(volRoute);
@@ -814,11 +816,6 @@
assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
}
- @Test
- public void testGetActiveSessions_returnsNonEmptyList() {
- assertFalse(mManager.getActiveSessions().isEmpty());
- }
-
Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> routeFeatures)
throws Exception {
CountDownLatch addedLatch = new CountDownLatch(1);
@@ -938,7 +935,7 @@
// ensure ManagerRecord in MediaRouter2ServiceImpl
addManagerCallback(new MediaRouter2Manager.Callback() {});
- for (RoutingSessionInfo session : mManager.getActiveSessions()) {
+ for (RoutingSessionInfo session : mManager.getRemoteSessions()) {
mManager.releaseSession(session);
}
}
diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp
index 38641de..deee5b1 100644
--- a/native/android/choreographer.cpp
+++ b/native/android/choreographer.cpp
@@ -40,6 +40,11 @@
return AChoreographer_routePostFrameCallbackDelayed64(choreographer, callback, data,
delayMillis);
}
+void AChoreographer_postExtendedFrameCallback(AChoreographer* choreographer,
+ AChoreographer_extendedFrameCallback callback,
+ void* data) {
+ return AChoreographer_routePostExtendedFrameCallback(choreographer, callback, data);
+}
void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
void* data) {
@@ -50,3 +55,27 @@
void* data) {
return AChoreographer_routeUnregisterRefreshRateCallback(choreographer, callback, data);
}
+int64_t AChoreographerFrameCallbackData_getFrameTimeNanos(
+ const AChoreographerFrameCallbackData* data) {
+ return AChoreographerFrameCallbackData_routeGetFrameTimeNanos(data);
+}
+size_t AChoreographerFrameCallbackData_getFrameTimelinesLength(
+ const AChoreographerFrameCallbackData* data) {
+ return AChoreographerFrameCallbackData_routeGetFrameTimelinesLength(data);
+}
+size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(
+ const AChoreographerFrameCallbackData* data) {
+ return AChoreographerFrameCallbackData_routeGetPreferredFrameTimelineIndex(data);
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineVsyncId(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ return AChoreographerFrameCallbackData_routeGetFrameTimelineVsyncId(data, index);
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ return AChoreographerFrameCallbackData_routeGetFrameTimelineExpectedPresentTime(data, index);
+}
+int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadline(
+ const AChoreographerFrameCallbackData* data, size_t index) {
+ return AChoreographerFrameCallbackData_routeGetFrameTimelineDeadline(data, index);
+}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index f33e118..29c1d8c 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -29,6 +29,13 @@
AChoreographer_postFrameCallbackDelayed64; # introduced=29
AChoreographer_registerRefreshRateCallback; # introduced=30
AChoreographer_unregisterRefreshRateCallback; # introduced=30
+ AChoreographer_postExtendedFrameCallback; # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimeNanos; # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelinesLength; # introduced=33
+ AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex; # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelineVsyncId; # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime; # introduced=33
+ AChoreographerFrameCallbackData_getFrameTimelineDeadline; # introduced=33
AConfiguration_copy;
AConfiguration_delete;
AConfiguration_diff;
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 7f74dd4..ceba4d6 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -366,11 +366,11 @@
sp<GraphicBuffer> graphic_buffer(reinterpret_cast<GraphicBuffer*>(buffer));
- transaction->setBuffer(surfaceControl, graphic_buffer);
+ std::optional<sp<Fence>> fence = std::nullopt;
if (acquire_fence_fd != -1) {
- sp<Fence> fence = new Fence(acquire_fence_fd);
- transaction->setAcquireFence(surfaceControl, fence);
+ fence = new Fence(acquire_fence_fd);
}
+ transaction->setBuffer(surfaceControl, graphic_buffer, fence);
}
void ASurfaceTransaction_setGeometry(ASurfaceTransaction* aSurfaceTransaction,
diff --git a/packages/PrintSpooler/res/values-te/strings.xml b/packages/PrintSpooler/res/values-te/strings.xml
index cf0e0f6..50e6f3b 100644
--- a/packages/PrintSpooler/res/values-te/strings.xml
+++ b/packages/PrintSpooler/res/values-te/strings.xml
@@ -50,8 +50,8 @@
<string name="search" msgid="5421724265322228497">"సెర్చ్"</string>
<string name="all_printers_label" msgid="3178848870161526399">"అన్ని ప్రింటర్లు"</string>
<string name="add_print_service_label" msgid="5356702546188981940">"సేవను జోడించు"</string>
- <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"శోధన పెట్టె చూపబడింది"</string>
- <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"శోధన పెట్టె దాచబడింది"</string>
+ <string name="print_search_box_shown_utterance" msgid="7967404953901376090">"సెర్చ్ బాక్స్ చూపబడింది"</string>
+ <string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"సెర్చ్ బాక్స్ దాచబడింది"</string>
<string name="print_add_printer" msgid="1088656468360653455">"ప్రింటర్ను జోడించు"</string>
<string name="print_select_printer" msgid="7388760939873368698">"ప్రింటర్ను ఎంచుకోండి"</string>
<string name="print_forget_printer" msgid="5035287497291910766">"ప్రింటర్ను విస్మరించు"</string>
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/config.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/config.xml
index 8993d0f..4860ad3 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/config.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/config.xml
@@ -17,4 +17,6 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<bool name="settingslib_config_icon_space_reserved">false</bool>
<bool name="settingslib_config_allow_divider">false</bool>
+ <!-- Name of a font family to use for headlines in SettingsLib. -->
+ <string name="settingslib_config_headlineFontFamily" translatable="false"></string>
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/strings.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/strings.xml
deleted file mode 100644
index 6d072a9..0000000
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/strings.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Name of a font family to use for headlines in SettingsLib. -->
- <string name="settingslib_config_headlineFontFamily" translatable="false">
- @*android:string/config_headlineFontFamily
- </string>
-
- <!-- Name of a font family to use for headlines-medium in SettingsLib. -->
- <string name="settingslib_config_headlineFontFamilyMedium" translatable="false">
- @*android:string/config_headlineFontFamilyMedium
- </string>
-
- <!-- Name of a font family to use for body in SettingsLib. -->
- <string name="settingslib_config_bodyFontFamily" translatable="false">
- @*android:string/config_bodyFontFamily
- </string>
-
- <!-- Name of a font family to use for body-medium in SettingsLib. -->
- <string name="settingslib_config_bodyFontFamilyMedium" translatable="false">
- @*android:string/config_bodyFontFamilyMedium
- </string>
-</resources>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index bbd89fe..d0fb2d4 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -585,7 +585,7 @@
<string name="user_new_profile_name" msgid="2405500423304678841">"নতুন প্ৰ\'ফাইল"</string>
<string name="user_info_settings_title" msgid="6351390762733279907">"ব্যৱহাৰকাৰীৰ তথ্য"</string>
<string name="profile_info_settings_title" msgid="105699672534365099">"প্ৰ\'ফাইলৰ তথ্য"</string>
- <string name="user_need_lock_message" msgid="4311424336209509301">"আপুনি সীমিত প্ৰ\'ফাইল এটা সৃষ্টি কৰাৰ আগেয়ে, আপোনাৰ ব্যক্তিগত ডেটা আৰু এপবিলাকক সুৰক্ষিত কৰিবলৈ স্ক্ৰীণ লক এটা নিৰ্ধাৰণ কৰিব লাগিব।"</string>
+ <string name="user_need_lock_message" msgid="4311424336209509301">"আপুনি সীমিত প্ৰ’ফাইল এটা সৃষ্টি কৰাৰ আগেয়ে, আপোনাৰ ব্যক্তিগত ডেটা আৰু এপ্বিলাকক সুৰক্ষিত কৰিবলৈ স্ক্ৰীন লক এটা নিৰ্ধাৰণ কৰিব লাগিব।"</string>
<string name="user_set_lock_button" msgid="1427128184982594856">"লক ছেট কৰক"</string>
<string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>লৈ সলনি কৰক"</string>
<string name="creating_new_user_dialog_message" msgid="7232880257538970375">"নতুন ব্যৱহাৰকাৰী সৃষ্টি কৰি থকা হৈছে…"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index fd39167..d3c13a9 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -117,7 +117,7 @@
<string name="bluetooth_profile_pbap" msgid="7064307749579335765">"కాంటాక్ట్ షేరింగ్"</string>
<string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"కాంటాక్ట్ షేరింగ్ కోసం ఉపయోగించండి"</string>
<string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"ఇంటర్నెట్ కనెక్షన్ షేరింగ్"</string>
- <string name="bluetooth_profile_map" msgid="8907204701162107271">"వచన మెసేజ్లు"</string>
+ <string name="bluetooth_profile_map" msgid="8907204701162107271">"టెక్స్ట్ మెసేజ్లు"</string>
<string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM యాక్సెస్"</string>
<string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ఆడియో: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ఆడియో"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 00a7b6c..db6b23b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -275,7 +275,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
@@ -316,7 +316,7 @@
}
mConnectAttempted = SystemClock.elapsedRealtime();
- connectAllEnabledProfiles();
+ connectDevice();
}
public long getHiSyncId() {
@@ -373,7 +373,7 @@
connect();
}
- private void connectAllEnabledProfiles() {
+ private void connectDevice() {
synchronized (mProfileLock) {
// Try to initialize the profiles if they were not.
if (mProfiles.isEmpty()) {
@@ -388,7 +388,7 @@
return;
}
- mLocalAdapter.connectAllEnabledProfiles(mDevice);
+ mDevice.connect();
}
}
@@ -772,8 +772,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/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 3c43f4a6..dd8f604 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -112,11 +112,9 @@
*/
boolean connectDeviceWithoutPackageName(MediaDevice device) {
boolean isConnected = false;
- final List<RoutingSessionInfo> infos = mRouterManager.getActiveSessions();
- if (infos.size() > 0) {
- final RoutingSessionInfo info = infos.get(0);
+ final RoutingSessionInfo info = mRouterManager.getSystemRoutingSession(null);
+ if (info != null) {
mRouterManager.transfer(info, device.mRouteInfo);
-
isConnected = true;
}
return isConnected;
@@ -414,7 +412,10 @@
}
List<RoutingSessionInfo> getActiveMediaSession() {
- return mRouterManager.getActiveSessions();
+ List<RoutingSessionInfo> infos = new ArrayList<>();
+ infos.add(mRouterManager.getSystemRoutingSession(null));
+ infos.addAll(mRouterManager.getRemoteSessions());
+ return infos;
}
private void buildAvailableRoutes() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 0d03f33..2d53831 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -237,7 +237,7 @@
final List<RoutingSessionInfo> infos = new ArrayList<>();
- mShadowRouter2Manager.setActiveSessions(infos);
+ mShadowRouter2Manager.setRemoteSessions(infos);
assertThat(mInfoMediaManager.connectDeviceWithoutPackageName(device)).isFalse();
}
@@ -525,10 +525,18 @@
@Test
public void getActiveMediaSession_returnActiveSession() {
+ RoutingSessionInfo sysSessionInfo = mock(RoutingSessionInfo.class);
final List<RoutingSessionInfo> infos = new ArrayList<>();
- mShadowRouter2Manager.setActiveSessions(infos);
+ infos.add(mock(RoutingSessionInfo.class));
+ final List<RoutingSessionInfo> activeSessionInfos = new ArrayList<>();
+ activeSessionInfos.add(sysSessionInfo);
+ activeSessionInfos.addAll(infos);
- assertThat(mInfoMediaManager.getActiveMediaSession()).containsExactlyElementsIn(infos);
+ mShadowRouter2Manager.setSystemRoutingSession(sysSessionInfo);
+ mShadowRouter2Manager.setRemoteSessions(infos);
+
+ assertThat(mInfoMediaManager.getActiveMediaSession())
+ .containsExactlyElementsIn(activeSessionInfos);
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
index 5959863..dac8142 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
@@ -16,6 +16,7 @@
package com.android.settingslib.testutils.shadow;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.media.RoutingSessionInfo;
@@ -33,8 +34,9 @@
private List<MediaRoute2Info> mAvailableRoutes;
private List<MediaRoute2Info> mAllRoutes;
private List<MediaRoute2Info> mDeselectableRoutes;
- private List<RoutingSessionInfo> mActiveSessions;
+ private List<RoutingSessionInfo> mRemoteSessions;
private List<RoutingSessionInfo> mRoutingSessions;
+ private RoutingSessionInfo mSystemRoutingSession;
@Implementation
protected List<MediaRoute2Info> getAvailableRoutes(String packageName) {
@@ -55,12 +57,12 @@
}
@Implementation
- protected List<RoutingSessionInfo> getActiveSessions() {
- return mActiveSessions;
+ protected List<RoutingSessionInfo> getRemoteSessions() {
+ return mRemoteSessions;
}
- public void setActiveSessions(List<RoutingSessionInfo> infos) {
- mActiveSessions = infos;
+ public void setRemoteSessions(List<RoutingSessionInfo> infos) {
+ mRemoteSessions = infos;
}
@Implementation
@@ -73,6 +75,15 @@
}
@Implementation
+ public RoutingSessionInfo getSystemRoutingSession(@Nullable String packageName) {
+ return mSystemRoutingSession;
+ }
+
+ public void setSystemRoutingSession(RoutingSessionInfo sessionInfo) {
+ mSystemRoutingSession = sessionInfo;
+ }
+
+ @Implementation
public List<MediaRoute2Info> getDeselectableRoutes(@NonNull RoutingSessionInfo sessionInfo) {
return mDeselectableRoutes;
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 54fb647..b9eec6e 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -554,6 +554,7 @@
<!-- Permission required for CTS test - SystemMediaRouter2Test -->
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
<!-- Permission required for CTS test - CtsRotationResolverServiceDeviceTestCases -->
<uses-permission android:name="android.permission.MANAGE_ROTATION_RESOLVER" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index fabc524..d7e4d72 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -50,6 +50,16 @@
srcs: ["src/com/android/systemui/EventLogTags.logtags"],
}
+filegroup {
+ name: "ReleaseJavaFiles",
+ srcs: ["src/com/android/systemui/flags/FeatureFlagManager.java"],
+}
+
+filegroup {
+ name: "DebugJavaFiles",
+ srcs: ["src-debug/com/android/systemui/flags/FeatureFlagManager.java"],
+}
+
android_library {
name: "SystemUI-core",
srcs: [
@@ -57,6 +67,12 @@
"src/**/*.java",
"src/**/I*.aidl",
],
+ product_variables: {
+ debuggable: {
+ srcs: [":DebugJavaFiles"],
+ exclude_srcs: [":ReleaseJavaFiles"],
+ },
+ },
resource_dirs: [
"res-product",
"res-keyguard",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index eb0bb77..8d02af1 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -443,6 +443,26 @@
</activity>
<!-- started from UsbDeviceSettingsManager -->
+ <activity android:name=".usb.tv.TvUsbConfirmActivity"
+ android:exported="true"
+ android:launchMode="singleTop"
+ android:permission="android.permission.MANAGE_USB"
+ android:theme="@style/BottomSheet"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
+
+ <!-- started from UsbDeviceSettingsManager -->
+ <activity android:name=".usb.tv.TvUsbPermissionActivity"
+ android:exported="true"
+ android:launchMode="singleTop"
+ android:permission="android.permission.MANAGE_USB"
+ android:theme="@style/BottomSheet"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
+
+ <!-- started from UsbDeviceSettingsManager -->
<activity android:name=".usb.UsbResolverActivity"
android:exported="true"
android:permission="android.permission.MANAGE_USB"
diff --git a/packages/SystemUI/animation/res/anim/launch_host_dialog_enter.xml b/packages/SystemUI/animation/res/anim/launch_host_dialog_enter.xml
new file mode 100644
index 0000000..c6b87d3
--- /dev/null
+++ b/packages/SystemUI/animation/res/anim/launch_host_dialog_enter.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<!-- The enter animation of the host dialog is a translation of 0px that lasts 500ms so that the -->
+<!-- host dialog is directly visible but the dim background still takes 500ms to fade in. -->
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:fromXDelta="0"
+ android:toXDelta="0"
+ android:duration="500" />
\ No newline at end of file
diff --git a/packages/SystemUI/animation/res/anim/launch_host_dialog_exit.xml b/packages/SystemUI/animation/res/anim/launch_host_dialog_exit.xml
new file mode 100644
index 0000000..a0f441e
--- /dev/null
+++ b/packages/SystemUI/animation/res/anim/launch_host_dialog_exit.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<alpha
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/decelerate_cubic"
+ android:duration="150"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0" />
\ No newline at end of file
diff --git a/packages/SystemUI/animation/res/values/ids.xml b/packages/SystemUI/animation/res/values/ids.xml
new file mode 100644
index 0000000..ef60a24
--- /dev/null
+++ b/packages/SystemUI/animation/res/values/ids.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<resources>
+ <item type="id" name="launch_animation_running"/>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/animation/res/values/styles.xml b/packages/SystemUI/animation/res/values/styles.xml
new file mode 100644
index 0000000..ad06c91
--- /dev/null
+++ b/packages/SystemUI/animation/res/values/styles.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<resources>
+ <style name="HostDialogTheme">
+ <item name="android:windowAnimationStyle">@style/Animation.HostDialog</item>
+ <item name="android:windowIsFloating">false</item>
+ <item name="android:backgroundDimEnabled">true</item>
+ <item name="android:navigationBarColor">@android:color/transparent</item>
+ </style>
+
+ <style name="Animation.HostDialog" parent="@android:style/Animation">
+ <item name="android:windowEnterAnimation">@anim/launch_host_dialog_enter</item>
+ <item name="android:windowExitAnimation">@anim/launch_host_dialog_exit</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 9c1e129..7020603 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -1,23 +1,31 @@
+/*
+ * 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.systemui.animation
-import android.animation.Animator
-import android.animation.AnimatorListenerAdapter
-import android.animation.ValueAnimator
import android.app.ActivityManager
import android.app.ActivityTaskManager
import android.app.PendingIntent
import android.app.TaskInfo
-import android.content.Context
import android.graphics.Matrix
-import android.graphics.PorterDuff
-import android.graphics.PorterDuffXfermode
import android.graphics.Rect
import android.graphics.RectF
-import android.graphics.drawable.GradientDrawable
import android.os.Looper
import android.os.RemoteException
import android.util.Log
-import android.util.MathUtils
import android.view.IRemoteAnimationFinishedCallback
import android.view.IRemoteAnimationRunner
import android.view.RemoteAnimationAdapter
@@ -26,7 +34,6 @@
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
-import android.view.animation.AnimationUtils
import android.view.animation.PathInterpolator
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
@@ -38,52 +45,23 @@
* A class that allows activities to be started in a seamless way from a view that is transforming
* nicely into the starting window.
*/
-class ActivityLaunchAnimator(
- private val callback: Callback,
- context: Context
-) {
+class ActivityLaunchAnimator(private val launchAnimator: LaunchAnimator) {
companion object {
- private const val DEBUG = false
- const val ANIMATION_DURATION = 500L
- private const val ANIMATION_DURATION_FADE_OUT_CONTENT = 150L
- private const val ANIMATION_DURATION_FADE_IN_WINDOW = 183L
- private const val ANIMATION_DELAY_FADE_IN_WINDOW = ANIMATION_DURATION_FADE_OUT_CONTENT
private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L
private const val ANIMATION_DELAY_NAV_FADE_IN =
- ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN
+ LaunchAnimator.ANIMATION_DURATION - ANIMATION_DURATION_NAV_FADE_IN
private const val LAUNCH_TIMEOUT = 1000L
- @JvmField val CONTENT_FADE_OUT_INTERPOLATOR = PathInterpolator(0f, 0f, 0.2f, 1f)
- private val WINDOW_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0.6f, 1f)
private val NAV_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0f, 1f)
private val NAV_FADE_OUT_INTERPOLATOR = PathInterpolator(0.2f, 0f, 1f, 1f)
-
- private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
-
- /**
- * Given the [linearProgress] of a launch animation, return the linear progress of the
- * sub-animation starting [delay] ms after the launch animation and that lasts [duration].
- */
- @JvmStatic
- fun getProgress(linearProgress: Float, delay: Long, duration: Long): Float {
- return MathUtils.constrain(
- (linearProgress * ANIMATION_DURATION - delay) / duration,
- 0.0f,
- 1.0f
- )
- }
}
- /** The interpolator used for the width, height, Y position and corner radius. */
- private val animationInterpolator = AnimationUtils.loadInterpolator(context,
- R.interpolator.launch_animation_interpolator_y)
-
- /** The interpolator used for the X position. */
- private val animationInterpolatorX = AnimationUtils.loadInterpolator(context,
- R.interpolator.launch_animation_interpolator_x)
-
- private val cornerRadii = FloatArray(8)
+ /**
+ * The callback of this animator. This should be set before any call to
+ * [start(Pending)IntentWithAnimation].
+ */
+ var callback: Callback? = null
/**
* Start an intent and animate the opening window. The intent will be started by running
@@ -119,6 +97,8 @@
return
}
+ val callback = this.callback ?: throw IllegalStateException(
+ "ActivityLaunchAnimator.callback must be set before using this animator")
val runner = Runner(controller)
val hideKeyguardWithAnimation = callback.isOnKeyguard() && !showOverLockscreen
@@ -126,9 +106,9 @@
// keyguard with the animation
val animationAdapter = if (!hideKeyguardWithAnimation) {
RemoteAnimationAdapter(
- runner,
- ANIMATION_DURATION,
- ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
+ runner,
+ LaunchAnimator.ANIMATION_DURATION,
+ LaunchAnimator.ANIMATION_DURATION - 150 /* statusBarTransitionDelay */
)
} else {
null
@@ -150,10 +130,10 @@
// Only animate if the app is not already on top and will be opened, unless we are on the
// keyguard.
val willAnimate =
- launchResult == ActivityManager.START_TASK_TO_FRONT ||
- launchResult == ActivityManager.START_SUCCESS ||
- (launchResult == ActivityManager.START_DELIVERED_TO_TOP &&
- hideKeyguardWithAnimation)
+ launchResult == ActivityManager.START_TASK_TO_FRONT ||
+ launchResult == ActivityManager.START_SUCCESS ||
+ (launchResult == ActivityManager.START_DELIVERED_TO_TOP &&
+ hideKeyguardWithAnimation)
Log.i(TAG, "launchResult=$launchResult willAnimate=$willAnimate " +
"hideKeyguardWithAnimation=$hideKeyguardWithAnimation")
@@ -234,7 +214,7 @@
*
* Note that all callbacks (onXXX methods) are all called on the main thread.
*/
- interface Controller {
+ interface Controller : LaunchAnimator.Controller {
companion object {
/**
* Return a [Controller] that will animate and expand [view] into the opening window.
@@ -259,53 +239,12 @@
}
/**
- * The container in which the view that started the intent will be animating together with
- * the opening window.
- *
- * This will be used to:
- * - Get the associated [Context].
- * - Compute whether we are expanding fully above the current window.
- * - Apply surface transactions in sync with RenderThread.
- *
- * This container can be changed to force this [Controller] to animate the expanding view
- * inside a different location, for instance to ensure correct layering during the
- * animation.
- */
- var launchContainer: ViewGroup
-
- /**
- * Return the [State] of the view that will be animated. We will animate from this state to
- * the final window state.
- *
- * Note: This state will be mutated and passed to [onLaunchAnimationProgress] during the
- * animation.
- */
- fun createAnimatorState(): State
-
- /**
* The intent was started. If [willAnimate] is false, nothing else will happen and the
* animation will not be started.
*/
fun onIntentStarted(willAnimate: Boolean) {}
/**
- * The animation started. This is typically used to initialize any additional resource
- * needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding
- * fully above the [root view][getRootView].
- */
- fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {}
-
- /** The animation made progress and the expandable view [state] should be updated. */
- fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {}
-
- /**
- * The animation ended. This will be called *if and only if* [onLaunchAnimationStart] was
- * called previously. This is typically used to clean up the resources initialized when the
- * animation was started.
- */
- fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {}
-
- /**
* The animation was cancelled. Note that [onLaunchAnimationEnd] will still be called after
* this if the animation was already started, i.e. if [onLaunchAnimationStart] was called
* before the cancellation.
@@ -313,66 +252,11 @@
fun onLaunchAnimationCancelled() {}
}
- /** The state of an expandable view during an [ActivityLaunchAnimator] animation. */
- open class State(
- /** The position of the view in screen space coordinates. */
- var top: Int,
- var bottom: Int,
- var left: Int,
- var right: Int,
-
- var topCornerRadius: Float = 0f,
- var bottomCornerRadius: Float = 0f
- ) {
- private val startTop = top
- private val startBottom = bottom
- private val startLeft = left
- private val startRight = right
- private val startWidth = width
- private val startHeight = height
- val startCenterX = centerX
- val startCenterY = centerY
-
- val width: Int
- get() = right - left
-
- val height: Int
- get() = bottom - top
-
- open val topChange: Int
- get() = top - startTop
-
- open val bottomChange: Int
- get() = bottom - startBottom
-
- val leftChange: Int
- get() = left - startLeft
-
- val rightChange: Int
- get() = right - startRight
-
- val widthRatio: Float
- get() = width.toFloat() / startWidth
-
- val heightRatio: Float
- get() = height.toFloat() / startHeight
-
- val centerX: Float
- get() = left + width / 2f
-
- val centerY: Float
- get() = top + height / 2f
-
- /** Whether the expanded view should be visible or hidden. */
- var visible: Boolean = true
- }
-
@VisibleForTesting
inner class Runner(private val controller: Controller) : IRemoteAnimationRunner.Stub() {
private val launchContainer = controller.launchContainer
private val context = launchContainer.context
private val transactionApplier = SyncRtSurfaceTransactionApplier(launchContainer)
- private var animator: ValueAnimator? = null
private val matrix = Matrix()
private val invertMatrix = Matrix()
@@ -380,6 +264,7 @@
private var windowCropF = RectF()
private var timedOut = false
private var cancelled = false
+ private var animation: LaunchAnimator.Animation? = null
// A timeout to cancel the remote animation if it is not started within X milliseconds after
// the intent was started.
@@ -429,7 +314,7 @@
nonApps: Array<out RemoteAnimationTarget>?,
iCallback: IRemoteAnimationFinishedCallback?
) {
- if (DEBUG) {
+ if (LaunchAnimator.DEBUG) {
Log.d(TAG, "Remote animation started")
}
@@ -449,36 +334,20 @@
it.windowType == WindowManager.LayoutParams.TYPE_NAVIGATION_BAR
}
- // Start state.
- val state = controller.createAnimatorState()
-
- val startTop = state.top
- val startBottom = state.bottom
- val startLeft = state.left
- val startRight = state.right
- val startXCenter = (startLeft + startRight) / 2f
- val startWidth = startRight - startLeft
-
- val startTopCornerRadius = state.topCornerRadius
- val startBottomCornerRadius = state.bottomCornerRadius
-
- // End state.
val windowBounds = window.screenSpaceBounds
- val endTop = windowBounds.top
- val endBottom = windowBounds.bottom
- val endLeft = windowBounds.left
- val endRight = windowBounds.right
- val endXCenter = (endLeft + endRight) / 2f
- val endWidth = endRight - endLeft
+ val endState = LaunchAnimator.State(
+ top = windowBounds.top,
+ bottom = windowBounds.bottom,
+ left = windowBounds.left,
+ right = windowBounds.right
+ )
+ val callback = this@ActivityLaunchAnimator.callback!!
+ val windowBackgroundColor = callback.getBackgroundColor(window.taskInfo)
- // TODO(b/184121838): Ensure that we are launching on the same screen.
- val rootViewLocation = launchContainer.locationOnScreen
- val isExpandingFullyAbove = endTop <= rootViewLocation[1] &&
- endBottom >= rootViewLocation[1] + launchContainer.height &&
- endLeft <= rootViewLocation[0] &&
- endRight >= rootViewLocation[0] + launchContainer.width
-
- // TODO(b/184121838): We should somehow get the top and bottom radius of the window.
+ // TODO(b/184121838): We should somehow get the top and bottom radius of the window
+ // instead of recomputing isExpandingFullyAbove here.
+ val isExpandingFullyAbove =
+ launchAnimator.isExpandingFullyAbove(controller.launchContainer, endState)
val endRadius = if (isExpandingFullyAbove) {
// Most of the time, expanding fully above the root view means expanding in full
// screen.
@@ -488,97 +357,40 @@
// a radius of 0.
0f
}
+ endState.topCornerRadius = endRadius
+ endState.bottomCornerRadius = endRadius
- // We add an extra layer with the same color as the app splash screen background color,
- // which is usually the same color of the app background. We first fade in this layer
- // to hide the expanding view, then we fade it out with SRC mode to draw a hole in the
- // launch container and reveal the opening window.
- val windowBackgroundColor = callback.getBackgroundColor(window.taskInfo)
- val windowBackgroundLayer = GradientDrawable().apply {
- setColor(windowBackgroundColor)
- alpha = 0
- }
-
- // Update state.
- val animator = ValueAnimator.ofFloat(0f, 1f)
- this.animator = animator
- animator.duration = ANIMATION_DURATION
- animator.interpolator = Interpolators.LINEAR
-
- val launchContainerOverlay = launchContainer.overlay
- animator.addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
- if (DEBUG) {
- Log.d(TAG, "Animation started")
- }
-
+ // We animate the opening window and delegate the view expansion to [this.controller].
+ val delegate = this.controller
+ val controller = object : LaunchAnimator.Controller by delegate {
+ override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
callback.setBlursDisabledForAppLaunch(true)
- controller.onLaunchAnimationStart(isExpandingFullyAbove)
-
- // Add the drawable to the launch container overlay. Overlays always draw
- // drawables after views, so we know that it will be drawn above any view added
- // by the controller.
- launchContainerOverlay.add(windowBackgroundLayer)
+ delegate.onLaunchAnimationStart(isExpandingFullyAbove)
}
- override fun onAnimationEnd(animation: Animator?) {
- if (DEBUG) {
- Log.d(TAG, "Animation ended")
- }
-
+ override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
callback.setBlursDisabledForAppLaunch(false)
iCallback?.invoke()
- controller.onLaunchAnimationEnd(isExpandingFullyAbove)
- launchContainerOverlay.remove(windowBackgroundLayer)
- }
- })
-
- animator.addUpdateListener { animation ->
- if (cancelled) {
- return@addUpdateListener
+ delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
}
- val linearProgress = animation.animatedFraction
- val progress = animationInterpolator.getInterpolation(linearProgress)
- val xProgress = animationInterpolatorX.getInterpolation(linearProgress)
- val xCenter = MathUtils.lerp(startXCenter, endXCenter, xProgress)
- val halfWidth = lerp(startWidth, endWidth, progress) / 2
-
- state.top = lerp(startTop, endTop, progress).roundToInt()
- state.bottom = lerp(startBottom, endBottom, progress).roundToInt()
- state.left = (xCenter - halfWidth).roundToInt()
- state.right = (xCenter + halfWidth).roundToInt()
-
- state.topCornerRadius = MathUtils.lerp(startTopCornerRadius, endRadius, progress)
- state.bottomCornerRadius =
- MathUtils.lerp(startBottomCornerRadius, endRadius, progress)
-
- // The expanding view can/should be hidden once it is completely coverred by the
- // windowBackgroundLayer.
- state.visible =
- getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT) < 1
-
- applyStateToWindow(window, state)
- applyStateToWindowBackgroundLayer(windowBackgroundLayer, state, linearProgress)
- navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
-
- // If we started expanding the view, we make it 1 pixel smaller on all sides to
- // avoid artefacts on the corners caused by anti-aliasing of the view background and
- // the window background layer.
- if (state.top != startTop && state.left != startLeft &&
- state.bottom != startBottom && state.right != startRight) {
- state.top += 1
- state.left += 1
- state.right -= 1
- state.bottom -= 1
+ override fun onLaunchAnimationProgress(
+ state: LaunchAnimator.State,
+ progress: Float,
+ linearProgress: Float
+ ) {
+ applyStateToWindow(window, state)
+ navigationBar?.let { applyStateToNavigationBar(it, state, linearProgress) }
+ delegate.onLaunchAnimationProgress(state, progress, linearProgress)
}
- controller.onLaunchAnimationProgress(state, progress, linearProgress)
}
- animator.start()
+ // We draw a hole when the additional layer is fading out to reveal the opening window.
+ animation = launchAnimator.startAnimation(
+ controller, endState, windowBackgroundColor, drawHole = true)
}
- private fun applyStateToWindow(window: RemoteAnimationTarget, state: State) {
+ private fun applyStateToWindow(window: RemoteAnimationTarget, state: LaunchAnimator.State) {
val screenBounds = window.screenSpaceBounds
val centerX = (screenBounds.left + screenBounds.right) / 2f
val centerY = (screenBounds.top + screenBounds.bottom) / 2f
@@ -632,48 +444,13 @@
transactionApplier.scheduleApply(params)
}
- private fun applyStateToWindowBackgroundLayer(
- drawable: GradientDrawable,
- state: State,
- linearProgress: Float
- ) {
- // Update position.
- drawable.setBounds(state.left, state.top, state.right, state.bottom)
-
- // Update radius.
- cornerRadii[0] = state.topCornerRadius
- cornerRadii[1] = state.topCornerRadius
- cornerRadii[2] = state.topCornerRadius
- cornerRadii[3] = state.topCornerRadius
- cornerRadii[4] = state.bottomCornerRadius
- cornerRadii[5] = state.bottomCornerRadius
- cornerRadii[6] = state.bottomCornerRadius
- cornerRadii[7] = state.bottomCornerRadius
- drawable.cornerRadii = cornerRadii
-
- // We first fade in the background layer to hide the expanding view, then fade it out
- // with SRC mode to draw a hole punch in the status bar and reveal the opening window.
- val fadeInProgress = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT)
- if (fadeInProgress < 1) {
- val alpha = CONTENT_FADE_OUT_INTERPOLATOR.getInterpolation(fadeInProgress)
- drawable.alpha = (alpha * 0xFF).roundToInt()
- drawable.setXfermode(null)
- } else {
- val fadeOutProgress = getProgress(linearProgress,
- ANIMATION_DELAY_FADE_IN_WINDOW, ANIMATION_DURATION_FADE_IN_WINDOW)
- val alpha = 1 - WINDOW_FADE_IN_INTERPOLATOR.getInterpolation(fadeOutProgress)
- drawable.alpha = (alpha * 0xFF).roundToInt()
- drawable.setXfermode(SRC_MODE)
- }
- }
-
private fun applyStateToNavigationBar(
navigationBar: RemoteAnimationTarget,
- state: State,
+ state: LaunchAnimator.State,
linearProgress: Float
) {
- val fadeInProgress = getProgress(linearProgress, ANIMATION_DELAY_NAV_FADE_IN,
- ANIMATION_DURATION_NAV_FADE_OUT)
+ val fadeInProgress = LaunchAnimator.getProgress(linearProgress,
+ ANIMATION_DELAY_NAV_FADE_IN, ANIMATION_DURATION_NAV_FADE_OUT)
val params = SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(navigationBar.leash)
if (fadeInProgress > 0) {
@@ -682,13 +459,13 @@
0f, (state.top - navigationBar.sourceContainerBounds.top).toFloat())
windowCrop.set(state.left, 0, state.right, state.height)
params
- .withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress))
- .withMatrix(matrix)
- .withWindowCrop(windowCrop)
- .withVisibility(true)
+ .withAlpha(NAV_FADE_IN_INTERPOLATOR.getInterpolation(fadeInProgress))
+ .withMatrix(matrix)
+ .withWindowCrop(windowCrop)
+ .withVisibility(true)
} else {
- val fadeOutProgress = getProgress(linearProgress, 0,
- ANIMATION_DURATION_NAV_FADE_OUT)
+ val fadeOutProgress = LaunchAnimator.getProgress(linearProgress, 0,
+ ANIMATION_DURATION_NAV_FADE_OUT)
params.withAlpha(1f - NAV_FADE_OUT_INTERPOLATOR.getInterpolation(fadeOutProgress))
}
@@ -714,7 +491,7 @@
cancelled = true
removeTimeout()
context.mainExecutor.execute {
- animator?.cancel()
+ animation?.cancel()
controller.onLaunchAnimationCancelled()
}
}
@@ -726,9 +503,5 @@
e.printStackTrace()
}
}
-
- private fun lerp(start: Int, stop: Int, amount: Float): Float {
- return MathUtils.lerp(start.toFloat(), stop.toFloat(), amount)
- }
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
index d4be253..258ca6b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DelegateLaunchAnimatorController.kt
@@ -1,3 +1,19 @@
+/*
+ * 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.systemui.animation
/**
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
new file mode 100644
index 0000000..c2b3608
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -0,0 +1,541 @@
+/*
+ * 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.systemui.animation
+
+import android.app.Dialog
+import android.content.Context
+import android.graphics.Color
+import android.os.Looper
+import android.util.Log
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewTreeObserver
+import android.view.WindowManager
+import android.widget.FrameLayout
+
+private const val TAG = "DialogLaunchAnimator"
+
+/**
+ * A class that allows dialogs to be started in a seamless way from a view that is transforming
+ * nicely into the starting dialog.
+ *
+ * Important: Don't forget to call [DialogLaunchAnimator.onDozeAmountChanged] when the doze amount
+ * changes to gracefully handle dialogs fading out when the device is dozing.
+ */
+class DialogLaunchAnimator(
+ private val context: Context,
+ private val launchAnimator: LaunchAnimator,
+ private val hostDialogProvider: HostDialogProvider
+) {
+ private companion object {
+ private val TAG_LAUNCH_ANIMATION_RUNNING = R.id.launch_animation_running
+ }
+
+ // TODO(b/201264644): Remove this set.
+ private val currentAnimations = hashSetOf<DialogLaunchAnimation>()
+
+ /**
+ * Show [dialog] by expanding it from [view].
+ *
+ * Caveats: When calling this function, the dialog content view will actually be stolen and
+ * attached to a different dialog (and thus a different window) which means that the actual
+ * dialog window will never be drawn. Moreover, unless [dialog] is a [ListenableDialog], you
+ * must call dismiss(), hide() and show() on the [Dialog] returned by this function to actually
+ * dismiss, hide or show the dialog.
+ */
+ fun showFromView(dialog: Dialog, view: View): Dialog {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ throw IllegalStateException(
+ "showFromView must be called from the main thread and dialog must be created in " +
+ "the main thread")
+ }
+
+ // Make sure we don't run the launch animation from the same view twice at the same time.
+ if (view.getTag(TAG_LAUNCH_ANIMATION_RUNNING) != null) {
+ Log.e(TAG, "Not running dialog launch animation as there is already one running")
+ dialog.show()
+ return dialog
+ }
+
+ view.setTag(TAG_LAUNCH_ANIMATION_RUNNING, true)
+
+ val launchAnimation = DialogLaunchAnimation(
+ context, launchAnimator, hostDialogProvider, view,
+ onDialogDismissed = { currentAnimations.remove(it) }, originalDialog = dialog)
+ val hostDialog = launchAnimation.hostDialog
+ currentAnimations.add(launchAnimation)
+
+ // If the dialog is dismissed/hidden/shown, then we should actually dismiss/hide/show the
+ // host dialog.
+ if (dialog is ListenableDialog) {
+ dialog.addListener(object : DialogListener {
+ override fun onDismiss() {
+ dialog.removeListener(this)
+ hostDialog.dismiss()
+ }
+
+ override fun onHide() {
+ if (launchAnimation.ignoreNextCallToHide) {
+ launchAnimation.ignoreNextCallToHide = false
+ return
+ }
+
+ hostDialog.hide()
+ }
+
+ override fun onShow() {
+ hostDialog.show()
+
+ // We don't actually want to show the original dialog, so hide it.
+ launchAnimation.ignoreNextCallToHide = true
+ dialog.hide()
+ }
+ })
+ }
+
+ launchAnimation.start()
+ return hostDialog
+ }
+
+ /** Notify the current doze amount, to ensure that dialogs fade out when dozing. */
+ // TODO(b/193634619): Replace this by some mandatory constructor parameter to make sure that we
+ // don't forget to call this when the doze amount changes.
+ fun onDozeAmountChanged(amount: Float) {
+ currentAnimations.forEach { it.onDozeAmountChanged(amount) }
+ }
+
+ /**
+ * Ensure that all dialogs currently shown won't animate into their touch surface when
+ * dismissed.
+ *
+ * This is a temporary API meant to be called right before we both dismiss a dialog and start
+ * an activity, which currently does not look good if we animate the dialog into the touch
+ * surface at the same time as the activity starts.
+ *
+ * TODO(b/193634619): Remove this function and animate dialog into opening activity instead.
+ */
+ fun disableAllCurrentDialogsExitAnimations() {
+ currentAnimations.forEach { it.exitAnimationDisabled = true }
+ }
+}
+
+interface HostDialogProvider {
+ /**
+ * Create a host dialog that will be used to host a launch animation. This host dialog must:
+ * 1. call [onCreateCallback] in its onCreate() method, e.g. right after calling
+ * super.onCreate().
+ * 2. call [dismissOverride] instead of doing any dismissing logic. The actual dismissing
+ * logic should instead be done inside the lambda passed to [dismissOverride], which will
+ * be called after the exit animation.
+ *
+ * See SystemUIHostDialogProvider for an example of implementation.
+ */
+ fun createHostDialog(
+ context: Context,
+ theme: Int,
+ onCreateCallback: () -> Unit,
+ dismissOverride: (() -> Unit) -> Unit
+ ): Dialog
+}
+
+/** A dialog to/from which we can add/remove listeners. */
+interface ListenableDialog {
+ /** Add [listener] to the listeners. */
+ fun addListener(listener: DialogListener)
+
+ /** Remove [listener] from the listeners. */
+ fun removeListener(listener: DialogListener)
+}
+
+interface DialogListener {
+ /** Called when this dialog dismiss() is called. */
+ fun onDismiss()
+
+ /** Called when this dialog hide() is called. */
+ fun onHide()
+
+ /** Called when this dialog show() is called. */
+ fun onShow()
+}
+
+private class DialogLaunchAnimation(
+ private val context: Context,
+ private val launchAnimator: LaunchAnimator,
+ hostDialogProvider: HostDialogProvider,
+
+ /** The view that triggered the dialog after being tapped. */
+ private val touchSurface: View,
+
+ /**
+ * A callback that will be called with this [DialogLaunchAnimation] after the dialog was
+ * dismissed and the exit animation is done.
+ */
+ private val onDialogDismissed: (DialogLaunchAnimation) -> Unit,
+
+ /** The original dialog whose content will be shown and animate in/out in [hostDialog]. */
+ private val originalDialog: Dialog
+) {
+ /**
+ * The fullscreen dialog to which we will add the content view [originalDialogView] of
+ * [originalDialog].
+ */
+ val hostDialog = hostDialogProvider.createHostDialog(
+ context, R.style.HostDialogTheme, this::onHostDialogCreated, this::onHostDialogDismissed)
+
+ /** The root content view of [hostDialog]. */
+ private val hostDialogRoot = FrameLayout(context)
+
+ /**
+ * The content view of [originalDialog], which will be stolen from that dialog and added to
+ * [hostDialogRoot].
+ */
+ private var originalDialogView: View? = null
+
+ /**
+ * The background color of [originalDialogView], taking into consideration the [originalDialog]
+ * window background color.
+ */
+ private var originalDialogBackgroundColor = Color.BLACK
+
+ /**
+ * Whether we are currently launching/showing the dialog by animating it from [touchSurface].
+ */
+ private var isLaunching = true
+
+ /** Whether we are currently dismissing/hiding the dialog by animating into [touchSurface]. */
+ private var isDismissing = false
+
+ private var dismissRequested = false
+ private var drawHostDialog = false
+ var ignoreNextCallToHide = false
+ var exitAnimationDisabled = false
+
+ fun start() {
+ // Show the host (fullscreen) dialog, to which we will add the stolen dialog view.
+ hostDialog.show()
+
+ // Steal the dialog view. We do that by showing it but preventing it from drawing, then
+ // hiding it as soon as its content is available.
+ stealOriginalDialogContentView(then = this::showDialogFromView)
+ }
+
+ private fun onHostDialogCreated() {
+ // Make the dialog fullscreen with a transparent background.
+ hostDialog.setContentView(
+ hostDialogRoot,
+ ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ )
+
+ val window = hostDialog.window
+ ?: throw IllegalStateException("There is no window associated to the host dialog")
+ window.setBackgroundDrawableResource(android.R.color.transparent)
+ window.setLayout(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.MATCH_PARENT
+ )
+
+ // Prevent the host dialog from drawing until the animation starts.
+ hostDialogRoot.viewTreeObserver.addOnPreDrawListener(
+ object : ViewTreeObserver.OnPreDrawListener {
+ override fun onPreDraw(): Boolean {
+ if (drawHostDialog) {
+ hostDialogRoot.viewTreeObserver.removeOnPreDrawListener(this)
+ return true
+ }
+
+ return false
+ }
+ }
+ )
+ }
+
+ /** Get the content view of [originalDialog] and pass it to [then]. */
+ private fun stealOriginalDialogContentView(then: (View) -> Unit) {
+ // The original dialog content view will be attached to android.R.id.content when the dialog
+ // is shown, so we show the dialog and add an observer to get the view but also prevents the
+ // original dialog from being drawn.
+ val androidContent = originalDialog.findViewById<ViewGroup>(android.R.id.content)
+ ?: throw IllegalStateException("Dialog does not have any android.R.id.content view")
+
+ androidContent.viewTreeObserver.addOnPreDrawListener(
+ object : ViewTreeObserver.OnPreDrawListener {
+ override fun onPreDraw(): Boolean {
+ if (androidContent.childCount == 1) {
+ androidContent.viewTreeObserver.removeOnPreDrawListener(this)
+
+ // Hide the animated dialog. Because of the dialog listener set up
+ // earlier, this would also hide the host dialog, but in this case we
+ // need to keep the host dialog visible.
+ ignoreNextCallToHide = true
+ originalDialog.hide()
+
+ then(androidContent.getChildAt(0))
+ return false
+ }
+
+ // Never draw the original dialog content.
+ return false
+ }
+ })
+ originalDialog.show()
+ }
+
+ private fun showDialogFromView(dialogView: View) {
+ // Save the dialog view for later as we will need it for the close animation.
+ this.originalDialogView = dialogView
+
+ // Close the dialog when clicking outside of it.
+ hostDialogRoot.setOnClickListener { hostDialog.dismiss() }
+ dialogView.isClickable = true
+
+ // Set the background of the window dialog to the dialog itself.
+ // TODO(b/193634619): Support dialog windows without background.
+ // TODO(b/193634619): Support dialog whose background comes from the content view instead of
+ // the window.
+ val typedArray =
+ originalDialog.context.obtainStyledAttributes(com.android.internal.R.styleable.Window)
+ val backgroundRes =
+ typedArray.getResourceId(com.android.internal.R.styleable.Window_windowBackground, 0)
+ typedArray.recycle()
+ if (backgroundRes == 0) {
+ throw IllegalStateException("Dialogs with no backgrounds on window are not supported")
+ }
+
+ dialogView.setBackgroundResource(backgroundRes)
+ originalDialogBackgroundColor =
+ GhostedViewLaunchAnimatorController.findGradientDrawable(dialogView.background!!)
+ ?.color
+ ?.defaultColor ?: Color.BLACK
+
+ // Add the dialog view to the host (fullscreen) dialog and make it invisible to make sure
+ // it's not drawn yet.
+ (dialogView.parent as? ViewGroup)?.removeView(dialogView)
+ hostDialogRoot.addView(
+ dialogView,
+
+ // We give it the size of its original dialog window.
+ FrameLayout.LayoutParams(
+ originalDialog.window.attributes.width,
+ originalDialog.window.attributes.height,
+ Gravity.CENTER
+ )
+ )
+ dialogView.visibility = View.INVISIBLE
+
+ // Start the animation when the dialog is laid out in the center of the host dialog.
+ dialogView.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
+ override fun onLayoutChange(
+ view: View,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ dialogView.removeOnLayoutChangeListener(this)
+ startAnimation(
+ isLaunching = true,
+ onLaunchAnimationStart = { drawHostDialog = true },
+ onLaunchAnimationEnd = {
+ touchSurface.setTag(R.id.launch_animation_running, null)
+
+ // We hide the touch surface when the dialog is showing. We will make this
+ // view visible again when dismissing the dialog.
+ // TODO(b/193634619): Provide an easy way for views to check if they should
+ // be hidden because of a dialog launch so that they don't override this
+ // visibility when updating/refreshing itself.
+ touchSurface.visibility = View.INVISIBLE
+
+ isLaunching = false
+
+ // dismiss was called during the animation, dismiss again now to actually
+ // dismiss.
+ if (dismissRequested) {
+ hostDialog.dismiss()
+ }
+ }
+ )
+ }
+ })
+ }
+
+ private fun onHostDialogDismissed(actualDismiss: () -> Unit) {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ context.mainExecutor.execute { onHostDialogDismissed(actualDismiss) }
+ return
+ }
+
+ // TODO(b/193634619): Support interrupting the launch animation in the middle.
+ if (isLaunching) {
+ dismissRequested = true
+ return
+ }
+
+ if (isDismissing) {
+ return
+ }
+
+ isDismissing = true
+ hideDialogIntoView { instantDismiss: Boolean ->
+ if (instantDismiss) {
+ originalDialog.hide()
+ hostDialog.hide()
+ }
+
+ originalDialog.dismiss()
+ actualDismiss()
+ }
+ }
+
+ /**
+ * Hide the dialog into the touch surface and call [dismissDialogs] when the animation is done
+ * (passing instantDismiss=true) or if it's skipped (passing instantDismiss=false) to actually
+ * dismiss the dialogs.
+ */
+ private fun hideDialogIntoView(dismissDialogs: (Boolean) -> Unit) {
+ if (!shouldAnimateDialogIntoView()) {
+ Log.i(TAG, "Skipping animation of dialog into the touch surface")
+
+ // If the view is invisible it's probably because of us, so we make it visible again.
+ if (touchSurface.visibility == View.INVISIBLE) {
+ touchSurface.visibility = View.VISIBLE
+ }
+
+ dismissDialogs(false /* instantDismiss */)
+ onDialogDismissed(this@DialogLaunchAnimation)
+ return
+ }
+
+ startAnimation(
+ isLaunching = false,
+ onLaunchAnimationStart = {
+ // Remove the dim background as soon as we start the animation.
+ hostDialog.window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+ },
+ onLaunchAnimationEnd = {
+ touchSurface.visibility = View.VISIBLE
+ originalDialogView!!.visibility = View.INVISIBLE
+ dismissDialogs(true /* instantDismiss */)
+ onDialogDismissed(this@DialogLaunchAnimation)
+ }
+ )
+ }
+
+ private fun startAnimation(
+ isLaunching: Boolean,
+ onLaunchAnimationStart: () -> Unit = {},
+ onLaunchAnimationEnd: () -> Unit = {}
+ ) {
+ val dialogView = this.originalDialogView!!
+
+ // Create 2 ghost controllers to animate both the dialog and the touch surface in the host
+ // dialog.
+ val startView = if (isLaunching) touchSurface else dialogView
+ val endView = if (isLaunching) dialogView else touchSurface
+ val startViewController = GhostedViewLaunchAnimatorController(startView)
+ val endViewController = GhostedViewLaunchAnimatorController(endView)
+ startViewController.launchContainer = hostDialogRoot
+ endViewController.launchContainer = hostDialogRoot
+
+ val endState = endViewController.createAnimatorState()
+ val controller = object : LaunchAnimator.Controller {
+ override var launchContainer: ViewGroup
+ get() = startViewController.launchContainer
+ set(value) {
+ startViewController.launchContainer = value
+ endViewController.launchContainer = value
+ }
+
+ override fun createAnimatorState(): LaunchAnimator.State {
+ return startViewController.createAnimatorState()
+ }
+
+ override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
+ startViewController.onLaunchAnimationStart(isExpandingFullyAbove)
+ endViewController.onLaunchAnimationStart(isExpandingFullyAbove)
+
+ onLaunchAnimationStart()
+ }
+
+ override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
+ startViewController.onLaunchAnimationEnd(isExpandingFullyAbove)
+ endViewController.onLaunchAnimationEnd(isExpandingFullyAbove)
+
+ onLaunchAnimationEnd()
+ }
+
+ override fun onLaunchAnimationProgress(
+ state: LaunchAnimator.State,
+ progress: Float,
+ linearProgress: Float
+ ) {
+ startViewController.onLaunchAnimationProgress(state, progress, linearProgress)
+
+ // The end view is visible only iff the starting view is not visible.
+ state.visible = !state.visible
+ endViewController.onLaunchAnimationProgress(state, progress, linearProgress)
+
+ // If the dialog content is complex, its dimension might change during the launch
+ // animation. The animation end position might also change during the exit
+ // animation, for instance when locking the phone when the dialog is open. Therefore
+ // we update the end state to the new position/size. Usually the dialog dimension or
+ // position will change in the early frames, so changing the end state shouldn't
+ // really be noticeable.
+ endViewController.fillGhostedViewState(endState)
+ }
+ }
+
+ launchAnimator.startAnimation(controller, endState, originalDialogBackgroundColor)
+ }
+
+ private fun shouldAnimateDialogIntoView(): Boolean {
+ if (exitAnimationDisabled) {
+ return false
+ }
+
+ // The touch surface should be invisible by now, if it's not then something else changed its
+ // visibility and we probably don't want to run the animation.
+ if (touchSurface.visibility != View.INVISIBLE) {
+ return false
+ }
+
+ // If the touch surface is not attached or one of its ancestors is not visible, then we
+ // don't run the animation either.
+ if (!touchSurface.isAttachedToWindow) {
+ return false
+ }
+
+ return (touchSurface.parent as? View)?.isShown ?: true
+ }
+
+ internal fun onDozeAmountChanged(amount: Float) {
+ val alpha = Interpolators.ALPHA_OUT.getInterpolation(1 - amount)
+ val decorView = this.hostDialog.window?.decorView ?: return
+ if (decorView.hasOverlappingRendering() && alpha > 0.0f &&
+ alpha < 1.0f && decorView.layerType != View.LAYER_TYPE_HARDWARE) {
+ decorView.setLayerType(View.LAYER_TYPE_HARDWARE, null)
+ }
+ decorView.alpha = alpha
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
index b4ffb3f..f7e0d58 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt
@@ -1,7 +1,24 @@
+/*
+ * 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.systemui.animation
import android.graphics.Canvas
import android.graphics.ColorFilter
+import android.graphics.Insets
import android.graphics.Matrix
import android.graphics.PixelFormat
import android.graphics.Rect
@@ -42,6 +59,7 @@
override var launchContainer = ghostedView.rootView as ViewGroup
private val launchContainerOverlay: ViewGroupOverlay
get() = launchContainer.overlay
+ private val launchContainerLocation = IntArray(2)
/** The ghost view that is drawn and animated instead of the ghosted view. */
private var ghostView: GhostView? = null
@@ -59,8 +77,12 @@
* [backgroundView].
*/
private var backgroundDrawable: WrappedDrawable? = null
+ private val backgroundInsets by lazy { getBackground()?.opticalInsets ?: Insets.NONE }
private var startBackgroundAlpha: Int = 0xFF
+ private val ghostedViewLocation = IntArray(2)
+ private val ghostedViewState = LaunchAnimator.State()
+
/**
* Return the background of the [ghostedView]. This background will be used to draw the
* background of the background view that is expanding up to the final animation position. This
@@ -103,16 +125,24 @@
return gradient.cornerRadii?.get(CORNER_RADIUS_BOTTOM_INDEX) ?: gradient.cornerRadius
}
- override fun createAnimatorState(): ActivityLaunchAnimator.State {
- val location = ghostedView.locationOnScreen
- return ActivityLaunchAnimator.State(
- top = location[1],
- bottom = location[1] + ghostedView.height,
- left = location[0],
- right = location[0] + ghostedView.width,
+ override fun createAnimatorState(): LaunchAnimator.State {
+ val state = LaunchAnimator.State(
topCornerRadius = getCurrentTopCornerRadius(),
bottomCornerRadius = getCurrentBottomCornerRadius()
)
+ fillGhostedViewState(state)
+ return state
+ }
+
+ fun fillGhostedViewState(state: LaunchAnimator.State) {
+ // For the animation we are interested in the area that has a non transparent background,
+ // so we have to take the optical insets into account.
+ ghostedView.getLocationOnScreen(ghostedViewLocation)
+ val insets = backgroundInsets
+ state.top = ghostedViewLocation[1] + insets.top
+ state.bottom = ghostedViewLocation[1] + ghostedView.height - insets.bottom
+ state.left = ghostedViewLocation[0] + insets.left
+ state.right = ghostedViewLocation[0] + ghostedView.width - insets.right
}
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
@@ -144,7 +174,7 @@
}
override fun onLaunchAnimationProgress(
- state: ActivityLaunchAnimator.State,
+ state: LaunchAnimator.State,
progress: Float,
linearProgress: Float
) {
@@ -162,19 +192,47 @@
return
}
- val scale = min(state.widthRatio, state.heightRatio)
- ghostViewMatrix.setValues(initialGhostViewMatrixValues)
- ghostViewMatrix.postScale(scale, scale, state.startCenterX, state.startCenterY)
+ // The ghost and backgrounds views were made invisible earlier. That can for instance happen
+ // when animating a dialog into a view.
+ if (ghostView.visibility == View.INVISIBLE) {
+ ghostView.visibility = View.VISIBLE
+ backgroundView.visibility = View.VISIBLE
+ }
+
+ fillGhostedViewState(ghostedViewState)
+ val leftChange = state.left - ghostedViewState.left
+ val rightChange = state.right - ghostedViewState.right
+ val topChange = state.top - ghostedViewState.top
+ val bottomChange = state.bottom - ghostedViewState.bottom
+
+ val widthRatio = state.width.toFloat() / ghostedViewState.width
+ val heightRatio = state.height.toFloat() / ghostedViewState.height
+ val scale = min(widthRatio, heightRatio)
+
+ launchContainer.getLocationOnScreen(launchContainerLocation)
+ GhostView.calculateMatrix(ghostedView, launchContainer, ghostViewMatrix)
+ ghostViewMatrix.postScale(
+ scale, scale,
+ ghostedViewState.centerX - launchContainerLocation[0],
+ ghostedViewState.centerY - launchContainerLocation[1]
+ )
ghostViewMatrix.postTranslate(
- (state.leftChange + state.rightChange) / 2f,
- (state.topChange + state.bottomChange) / 2f
+ (leftChange + rightChange) / 2f,
+ (topChange + bottomChange) / 2f
)
ghostView.animationMatrix = ghostViewMatrix
- backgroundView.top = state.top
- backgroundView.bottom = state.bottom
- backgroundView.left = state.left
- backgroundView.right = state.right
+ // We need to take into account the background insets for the background position.
+ val insets = backgroundInsets
+ val topWithInsets = state.top - insets.top
+ val leftWithInsets = state.left - insets.left
+ val rightWithInsets = state.right + insets.right
+ val bottomWithInsets = state.bottom + insets.bottom
+
+ backgroundView.top = topWithInsets - launchContainerLocation[1]
+ backgroundView.bottom = bottomWithInsets - launchContainerLocation[1]
+ backgroundView.left = leftWithInsets - launchContainerLocation[0]
+ backgroundView.right = rightWithInsets - launchContainerLocation[0]
val backgroundDrawable = backgroundDrawable!!
backgroundDrawable.wrapped?.let {
@@ -207,7 +265,7 @@
* [drawable] is a [LayerDrawable], this will return the first layer that is a
* [GradientDrawable].
*/
- private fun findGradientDrawable(drawable: Drawable): GradientDrawable? {
+ fun findGradientDrawable(drawable: Drawable): GradientDrawable? {
if (drawable is GradientDrawable) {
return drawable
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
index 659b9fe..2765882 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Interpolators.java
@@ -16,6 +16,7 @@
package com.android.systemui.animation;
+import android.graphics.Path;
import android.util.MathUtils;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
@@ -29,7 +30,97 @@
* Utility class to receive interpolators from
*/
public class Interpolators {
- public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+ /*
+ * ============================================================================================
+ * Emphasized interpolators.
+ * ============================================================================================
+ */
+
+ /**
+ * The default emphasized interpolator. Used for hero / emphasized movement of content.
+ */
+ public static final Interpolator EMPHASIZED = createEmphasizedInterpolator();
+
+ /**
+ * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
+ * is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator(
+ 0.3f, 0f, 0.8f, 0.15f);
+
+ /**
+ * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
+ * is appearing e.g. when coming from off screen
+ */
+ public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator(
+ 0.05f, 0.7f, 0.1f, 1f);
+
+
+ /*
+ * ============================================================================================
+ * Standard interpolators.
+ * ============================================================================================
+ */
+
+ /**
+ * The standard interpolator that should be used on every normal animation
+ */
+ public static final Interpolator STANDARD = new PathInterpolator(
+ 0.2f, 0f, 0f, 1f);
+
+ /**
+ * The standard accelerating interpolator that should be used on every regular movement of
+ * content that is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator(
+ 0.3f, 0f, 1f, 1f);
+
+ /**
+ * The standard decelerating interpolator that should be used on every regular movement of
+ * content that is appearing e.g. when coming from off screen.
+ */
+ public static final Interpolator STANDARD_DECELERATE = new PathInterpolator(
+ 0f, 0f, 0f, 1f);
+
+ /*
+ * ============================================================================================
+ * Legacy
+ * ============================================================================================
+ */
+
+ /**
+ * The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN.
+ */
+ public static final Interpolator LEGACY = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+
+ /**
+ * The default legacy accelerating interpolator as defined in Material 1.
+ * Also known as FAST_OUT_LINEAR_IN.
+ */
+ public static final Interpolator LEGACY_ACCELERATE = new PathInterpolator(0.4f, 0f, 1f, 1f);
+
+ /**
+ * The default legacy decelerating interpolator as defined in Material 1.
+ * Also known as LINEAR_OUT_SLOW_IN.
+ */
+ public static final Interpolator LEGACY_DECELERATE = new PathInterpolator(0f, 0f, 0.2f, 1f);
+
+ /**
+ * Linear interpolator. Often used if the interpolator is for different properties who need
+ * different interpolations.
+ */
+ public static final Interpolator LINEAR = new LinearInterpolator();
+
+ /*
+ * ============================================================================================
+ * Custom interpolators
+ * ============================================================================================
+ */
+
+ public static final Interpolator FAST_OUT_SLOW_IN = LEGACY;
+ public static final Interpolator FAST_OUT_LINEAR_IN = LEGACY_ACCELERATE;
+ public static final Interpolator LINEAR_OUT_SLOW_IN = LEGACY_DECELERATE;
/**
* Like {@link #FAST_OUT_SLOW_IN}, but used in case the animation is played in reverse (i.e. t
@@ -37,12 +128,9 @@
*/
public static final Interpolator FAST_OUT_SLOW_IN_REVERSE =
new PathInterpolator(0.8f, 0f, 0.6f, 1f);
- public static final Interpolator FAST_OUT_LINEAR_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
- public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f);
public static final Interpolator SLOW_OUT_LINEAR_IN = new PathInterpolator(0.8f, 0f, 1f, 1f);
public static final Interpolator ALPHA_IN = new PathInterpolator(0.4f, 0f, 1f, 1f);
public static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f);
- public static final Interpolator LINEAR = new LinearInterpolator();
public static final Interpolator ACCELERATE = new AccelerateInterpolator();
public static final Interpolator ACCELERATE_DECELERATE = new AccelerateDecelerateInterpolator();
public static final Interpolator DECELERATE_QUINT = new DecelerateInterpolator(2.5f);
@@ -72,6 +160,12 @@
public static final Interpolator TOUCH_RESPONSE_REVERSE =
new PathInterpolator(0.9f, 0f, 0.7f, 1f);
+ /*
+ * ============================================================================================
+ * Functions / Utilities
+ * ============================================================================================
+ */
+
/**
* Calculate the amount of overshoot using an exponential falloff function with desired
* properties, where the overshoot smoothly transitions at the 1.0f boundary into the
@@ -122,4 +216,14 @@
return (float) (1f - 0.5f * (1f - Math.cos(3.14159f * oneMinusFrac * oneMinusFrac)));
}
}
+
+ // Create the default emphasized interpolator
+ private static PathInterpolator createEmphasizedInterpolator() {
+ Path path = new Path();
+ // Doing the same as fast_out_extra_slow_in
+ path.moveTo(0f, 0f);
+ path.cubicTo(0.05f, 0f, 0.133333f, 0.06f, 0.166666f, 0.4f);
+ path.cubicTo(0.208333f, 0.82f, 0.25f, 1f, 1f, 1f);
+ return new PathInterpolator(path);
+ }
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
new file mode 100644
index 0000000..3bf6c5e
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
@@ -0,0 +1,355 @@
+/*
+ * 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.systemui.animation
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffXfermode
+import android.graphics.drawable.GradientDrawable
+import android.util.Log
+import android.util.MathUtils
+import android.view.View
+import android.view.ViewGroup
+import android.view.animation.AnimationUtils
+import android.view.animation.PathInterpolator
+import kotlin.math.roundToInt
+
+private const val TAG = "LaunchAnimator"
+
+/** A base class to animate a window launch (activity or dialog) from a view . */
+class LaunchAnimator @JvmOverloads constructor(
+ context: Context,
+ private val isForTesting: Boolean = false
+) {
+ companion object {
+ internal const val DEBUG = false
+ const val ANIMATION_DURATION = 500L
+ private const val ANIMATION_DURATION_FADE_OUT_CONTENT = 150L
+ private const val ANIMATION_DURATION_FADE_IN_WINDOW = 183L
+ private const val ANIMATION_DELAY_FADE_IN_WINDOW = ANIMATION_DURATION_FADE_OUT_CONTENT
+
+ private val WINDOW_FADE_IN_INTERPOLATOR = PathInterpolator(0f, 0f, 0.6f, 1f)
+ private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
+
+ /**
+ * Given the [linearProgress] of a launch animation, return the linear progress of the
+ * sub-animation starting [delay] ms after the launch animation and that lasts [duration].
+ */
+ @JvmStatic
+ fun getProgress(linearProgress: Float, delay: Long, duration: Long): Float {
+ return MathUtils.constrain(
+ (linearProgress * ANIMATION_DURATION - delay) / duration,
+ 0.0f,
+ 1.0f
+ )
+ }
+ }
+
+ /** The interpolator used for the width, height, Y position and corner radius. */
+ private val animationInterpolator = AnimationUtils.loadInterpolator(context,
+ R.interpolator.launch_animation_interpolator_y)
+
+ /** The interpolator used for the X position. */
+ private val animationInterpolatorX = AnimationUtils.loadInterpolator(context,
+ R.interpolator.launch_animation_interpolator_x)
+
+ private val launchContainerLocation = IntArray(2)
+ private val cornerRadii = FloatArray(8)
+
+ /**
+ * A controller that takes care of applying the animation to an expanding view.
+ *
+ * Note that all callbacks (onXXX methods) are all called on the main thread.
+ */
+ interface Controller {
+ /**
+ * The container in which the view that started the animation will be animating together
+ * with the opening window.
+ *
+ * This will be used to:
+ * - Get the associated [Context].
+ * - Compute whether we are expanding fully above the launch container.
+ * - Apply surface transactions in sync with RenderThread when animating an activity
+ * launch.
+ *
+ * This container can be changed to force this [Controller] to animate the expanding view
+ * inside a different location, for instance to ensure correct layering during the
+ * animation.
+ */
+ var launchContainer: ViewGroup
+
+ /**
+ * Return the [State] of the view that will be animated. We will animate from this state to
+ * the final window state.
+ *
+ * Note: This state will be mutated and passed to [onLaunchAnimationProgress] during the
+ * animation.
+ */
+ fun createAnimatorState(): State
+
+ /**
+ * The animation started. This is typically used to initialize any additional resource
+ * needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding
+ * fully above the [launchContainer].
+ */
+ fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {}
+
+ /** The animation made progress and the expandable view [state] should be updated. */
+ fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {}
+
+ /**
+ * The animation ended. This will be called *if and only if* [onLaunchAnimationStart] was
+ * called previously. This is typically used to clean up the resources initialized when the
+ * animation was started.
+ */
+ fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {}
+ }
+
+ /** The state of an expandable view during a [LaunchAnimator] animation. */
+ open class State(
+ /** The position of the view in screen space coordinates. */
+ var top: Int = 0,
+ var bottom: Int = 0,
+ var left: Int = 0,
+ var right: Int = 0,
+
+ var topCornerRadius: Float = 0f,
+ var bottomCornerRadius: Float = 0f
+ ) {
+ private val startTop = top
+
+ val width: Int
+ get() = right - left
+
+ val height: Int
+ get() = bottom - top
+
+ open val topChange: Int
+ get() = top - startTop
+
+ val centerX: Float
+ get() = left + width / 2f
+
+ val centerY: Float
+ get() = top + height / 2f
+
+ /** Whether the expanding view should be visible or hidden. */
+ var visible: Boolean = true
+ }
+
+ interface Animation {
+ /** Cancel the animation. */
+ fun cancel()
+ }
+
+ /**
+ * Start a launch animation controlled by [controller] towards [endState]. An intermediary
+ * layer with [windowBackgroundColor] will fade in then fade out above the expanding view, and
+ * should be the same background color as the opening (or closing) window. If [drawHole] is
+ * true, then this intermediary layer will be drawn with SRC blending mode while it fades out.
+ *
+ * TODO(b/184121838): Remove [drawHole] and instead make the StatusBar draw this hole instead.
+ */
+ fun startAnimation(
+ controller: Controller,
+ endState: State,
+ windowBackgroundColor: Int,
+ drawHole: Boolean = false
+ ): Animation {
+ val state = controller.createAnimatorState()
+
+ // Start state.
+ val startTop = state.top
+ val startBottom = state.bottom
+ val startLeft = state.left
+ val startRight = state.right
+ val startCenterX = (startLeft + startRight) / 2f
+ val startWidth = startRight - startLeft
+ val startTopCornerRadius = state.topCornerRadius
+ val startBottomCornerRadius = state.bottomCornerRadius
+
+ // End state.
+ var endTop = endState.top
+ var endBottom = endState.bottom
+ var endLeft = endState.left
+ var endRight = endState.right
+ var endCenterX = (endLeft + endRight) / 2f
+ var endWidth = endRight - endLeft
+ val endTopCornerRadius = endState.topCornerRadius
+ val endBottomCornerRadius = endState.bottomCornerRadius
+
+ fun maybeUpdateEndState() {
+ if (endTop != endState.top || endBottom != endState.bottom ||
+ endLeft != endState.left || endRight != endState.right) {
+ endTop = endState.top
+ endBottom = endState.bottom
+ endLeft = endState.left
+ endRight = endState.right
+ endCenterX = (endLeft + endRight) / 2f
+ endWidth = endRight - endLeft
+ }
+ }
+
+ val launchContainer = controller.launchContainer
+ val isExpandingFullyAbove = isExpandingFullyAbove(launchContainer, endState)
+
+ // We add an extra layer with the same color as the dialog/app splash screen background
+ // color, which is usually the same color of the app background. We first fade in this layer
+ // to hide the expanding view, then we fade it out with SRC mode to draw a hole in the
+ // launch container and reveal the opening window.
+ val windowBackgroundLayer = GradientDrawable().apply {
+ setColor(windowBackgroundColor)
+ alpha = 0
+ }
+
+ // Update state.
+ val animator = ValueAnimator.ofFloat(0f, 1f)
+ animator.duration = if (isForTesting) 0 else ANIMATION_DURATION
+ animator.interpolator = Interpolators.LINEAR
+
+ val launchContainerOverlay = launchContainer.overlay
+ var cancelled = false
+ animator.addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
+ if (DEBUG) {
+ Log.d(TAG, "Animation started")
+ }
+ controller.onLaunchAnimationStart(isExpandingFullyAbove)
+
+ // Add the drawable to the launch container overlay. Overlays always draw
+ // drawables after views, so we know that it will be drawn above any view added
+ // by the controller.
+ launchContainerOverlay.add(windowBackgroundLayer)
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ if (DEBUG) {
+ Log.d(TAG, "Animation ended")
+ }
+ controller.onLaunchAnimationEnd(isExpandingFullyAbove)
+ launchContainerOverlay.remove(windowBackgroundLayer)
+ }
+ })
+
+ animator.addUpdateListener { animation ->
+ if (cancelled) {
+ // TODO(b/184121838): Cancel the animator directly instead of just skipping the
+ // update.
+ return@addUpdateListener
+ }
+
+ maybeUpdateEndState()
+
+ // TODO(b/184121838): Use reverse interpolators to get the same path/arc as the non
+ // reversed animation.
+ val linearProgress = animation.animatedFraction
+ val progress = animationInterpolator.getInterpolation(linearProgress)
+ val xProgress = animationInterpolatorX.getInterpolation(linearProgress)
+
+ val xCenter = MathUtils.lerp(startCenterX, endCenterX, xProgress)
+ val halfWidth = MathUtils.lerp(startWidth, endWidth, progress) / 2f
+
+ state.top = MathUtils.lerp(startTop, endTop, progress).roundToInt()
+ state.bottom = MathUtils.lerp(startBottom, endBottom, progress).roundToInt()
+ state.left = (xCenter - halfWidth).roundToInt()
+ state.right = (xCenter + halfWidth).roundToInt()
+
+ state.topCornerRadius =
+ MathUtils.lerp(startTopCornerRadius, endTopCornerRadius, progress)
+ state.bottomCornerRadius =
+ MathUtils.lerp(startBottomCornerRadius, endBottomCornerRadius, progress)
+
+ // The expanding view can/should be hidden once it is completely covered by the opening
+ // window.
+ state.visible = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT) < 1
+
+ applyStateToWindowBackgroundLayer(
+ windowBackgroundLayer,
+ state,
+ linearProgress,
+ launchContainer,
+ drawHole
+ )
+ controller.onLaunchAnimationProgress(state, progress, linearProgress)
+ }
+
+ animator.start()
+ return object : Animation {
+ override fun cancel() {
+ cancelled = true
+ animator.cancel()
+ }
+ }
+ }
+
+ /** Return whether we are expanding fully above the [launchContainer]. */
+ internal fun isExpandingFullyAbove(launchContainer: View, endState: State): Boolean {
+ launchContainer.getLocationOnScreen(launchContainerLocation)
+ return endState.top <= launchContainerLocation[1] &&
+ endState.bottom >= launchContainerLocation[1] + launchContainer.height &&
+ endState.left <= launchContainerLocation[0] &&
+ endState.right >= launchContainerLocation[0] + launchContainer.width
+ }
+
+ private fun applyStateToWindowBackgroundLayer(
+ drawable: GradientDrawable,
+ state: State,
+ linearProgress: Float,
+ launchContainer: View,
+ drawHole: Boolean
+ ) {
+ // Update position.
+ launchContainer.getLocationOnScreen(launchContainerLocation)
+ drawable.setBounds(
+ state.left - launchContainerLocation[0],
+ state.top - launchContainerLocation[1],
+ state.right - launchContainerLocation[0],
+ state.bottom - launchContainerLocation[1]
+ )
+
+ // Update radius.
+ cornerRadii[0] = state.topCornerRadius
+ cornerRadii[1] = state.topCornerRadius
+ cornerRadii[2] = state.topCornerRadius
+ cornerRadii[3] = state.topCornerRadius
+ cornerRadii[4] = state.bottomCornerRadius
+ cornerRadii[5] = state.bottomCornerRadius
+ cornerRadii[6] = state.bottomCornerRadius
+ cornerRadii[7] = state.bottomCornerRadius
+ drawable.cornerRadii = cornerRadii
+
+ // We first fade in the background layer to hide the expanding view, then fade it out
+ // with SRC mode to draw a hole punch in the status bar and reveal the opening window.
+ val fadeInProgress = getProgress(linearProgress, 0, ANIMATION_DURATION_FADE_OUT_CONTENT)
+ if (fadeInProgress < 1) {
+ val alpha = Interpolators.LINEAR_OUT_SLOW_IN.getInterpolation(fadeInProgress)
+ drawable.alpha = (alpha * 0xFF).roundToInt()
+ } else {
+ val fadeOutProgress = getProgress(
+ linearProgress, ANIMATION_DELAY_FADE_IN_WINDOW, ANIMATION_DURATION_FADE_IN_WINDOW)
+ val alpha = 1 - WINDOW_FADE_IN_INTERPOLATOR.getInterpolation(fadeOutProgress)
+ drawable.alpha = (alpha * 0xFF).roundToInt()
+
+ if (drawHole) {
+ drawable.setXfermode(SRC_MODE)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml b/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml
new file mode 100644
index 0000000..08c66a2
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/color/numpad_key_color_secondary.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <item android:color="?androidprv:attr/colorAccentSecondaryVariant"/>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml b/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml
index dd35dd9..1eec820 100644
--- a/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml
+++ b/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:fillColor="?android:attr/colorBackground"
+ android:fillColor="?android:attr/textColorPrimaryInverse"
android:pathData="M9,15.59L12.59,12L9,8.41L10.41,7L14,10.59L17.59,7L19,8.41L15.41,12L19,15.59L17.59,17L14,13.41L10.41,17L9,15.59zM21,6H8l-4.5,6L8,18h13V6M21,4c1.1,0 2,0.9 2,2v12c0,1.1 -0.9,2 -2,2H8c-0.63,0 -1.22,-0.3 -1.6,-0.8L1,12l5.4,-7.2C6.78,4.3 7.37,4 8,4H21L21,4z"/>
</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml b/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml
index b844515..2ad5e54 100644
--- a/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml
+++ b/packages/SystemUI/res-keyguard/drawable/ic_keyboard_tab_36dp.xml
@@ -19,7 +19,8 @@
android:viewportHeight="36"
android:viewportWidth="36"
android:width="36sp">
- <path android:fillColor="?android:attr/colorBackground"
+
+ <path android:fillColor="?android:attr/textColorPrimaryInverse"
android:pathData="M17.59,13.41L21.17,17H7v2h14.17l-3.59,3.59L19,24l6,-6l-6,-6L17.59,
13.41zM26,12v12h2V12H26z"/>
</vector>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index 624ee9f..6e89fb0 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -17,7 +17,7 @@
*/
-->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Keyguard PIN pad styles -->
<style name="Keyguard.TextView" parent="@android:style/Widget.DeviceDefault.TextView">
<item name="android:textSize">@dimen/kg_status_line_font_size</item>
@@ -58,11 +58,11 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="NumPadKey.Delete">
- <item name="android:colorControlNormal">?android:attr/textColorSecondary</item>
+ <item name="android:colorControlNormal">@color/numpad_key_color_secondary</item>
<item name="android:src">@drawable/ic_backspace_24dp</item>
</style>
<style name="NumPadKey.Enter">
- <item name="android:colorControlNormal">?android:attr/textColorSecondary</item>
+ <item name="android:colorControlNormal">@color/numpad_key_color_secondary</item>
<item name="android:src">@drawable/ic_keyboard_tab_36dp</item>
</style>
<style name="Widget.TextView.NumPadKey.Klondike"
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_background.xml
deleted file mode 100644
index 3ceb0f6..0000000
--- a/packages/SystemUI/res/drawable/media_output_dialog_background.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-
-<inset xmlns:android="http://schemas.android.com/apk/res/android">
- <shape android:shape="rectangle">
- <corners android:radius="8dp" />
- <solid android:color="?android:attr/colorBackground" />
- </shape>
-</inset>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 918635d..c1d7308b 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -19,7 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:id="@+id/internet_connectivity_dialog"
- android:layout_width="@dimen/internet_dialog_list_max_width"
+ android:layout_width="@dimen/large_dialog_width"
android:layout_height="@dimen/internet_dialog_list_max_height"
android:background="@drawable/internet_dialog_rounded_top_corner_background"
android:orientation="vertical">
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index d996cee..b338894 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -18,7 +18,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/media_output_dialog"
- android:layout_width="match_parent"
+ android:layout_width="@dimen/large_dialog_width"
android:layout_height="wrap_content"
android:orientation="vertical">
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index 566cd25..b546a9c 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -134,6 +134,7 @@
android:background="@drawable/qs_media_light_source"
android:forceHasOverlappingRendering="false">
<LinearLayout
+ android:id="@+id/media_seamless_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="@dimen/qs_seamless_height"
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index f571fa6..e2d5229 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -130,13 +130,4 @@
app:layout_constraintStart_toStartOf="@id/screenshot_preview"
app:layout_constraintTop_toTopOf="@id/screenshot_preview"
android:elevation="@dimen/screenshot_preview_elevation"/>
- <View
- android:id="@+id/screenshot_transition_view"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:visibility="invisible"
- app:layout_constraintStart_toStartOf="@id/screenshot_preview"
- app:layout_constraintTop_toTopOf="@id/screenshot_preview"
- app:layout_constraintEnd_toEndOf="@id/screenshot_preview"
- app:layout_constraintBottom_toBottomOf="@id/screenshot_preview"/>
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index f82eb11..a58e12f 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -29,12 +29,6 @@
<include layout="@layout/communal_host_view"
android:visibility="gone"/>
- <FrameLayout
- android:id="@+id/big_clock_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
-
<ViewStub
android:id="@+id/keyguard_qs_user_switch_stub"
android:layout="@layout/keyguard_qs_user_switch"
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 68cd774..23bc620 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"አውታረ መረቦችን ለመመልከት ይክፈቱ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"አውታረ መረቦችን በመፈለግ ላይ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ከአውታረ መረቡ ጋር መገናኘት አልተሳካም"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wifi ለአሁን በራስ-ሰር አይገናኝም"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ሁሉንም ይመልከቱ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"አውታረ መረቦችን ለመቀየር፣ የኢተርኔት ግንኙነት ያቋርጡ"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> የሚከተለውን ሰቅ ወደ ፈጣን ቅንብሮች ማከል ይፈልጋል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 13ebcef..7495a49 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -1199,8 +1199,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"فتح القفل لعرض الشبكات"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"جارٍ البحث عن شبكات…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"تعذّر الاتصال بالشبكة."</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"لن يتم الاتصال بشبكة Wi-Fi تلقائيًا في الوقت الحالي."</string>
<string name="see_all_networks" msgid="3773666844913168122">"عرض الكل"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"للتبديل بين الشبكات، يجب فصل إيثرنت."</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"يريد تطبيق <xliff:g id="APPNAME">%1$s</xliff:g> إضافة المربّع التالي إلى \"الإعدادات السريعة\""</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 492198d..43f5758 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"নেটৱর্ক চাবলৈ আনলক কৰক"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটৱৰ্ক সন্ধান কৰি থকা হৈছে…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটৱৰ্কৰ সৈতে সংযোগ কৰিব পৰা নগ\'ল"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"এতিয়া ৱাই-ফাই স্বয়ংক্ৰিয়ভাৱে সংযুক্ত নহ’ব"</string>
<string name="see_all_networks" msgid="3773666844913168122">"আটাইবোৰ চাওক"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটৱৰ্ক সলনি কৰিবলৈ ইথাৰনেটৰ পৰা সংযোগ বিচ্ছিন্ন কৰক"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g>এ ক্ষিপ্ৰ ছেটিঙত এই টাইলটো যোগ দিব বিচাৰিছে"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 7b107e0..6909e6c 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Şəbəkələrə baxmaq üçün kilidi açın"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Şəbəkə axtarılır…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Şəbəkəyə qoşulmaq alınmadı"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi hələlik avtomatik qoşulmayacaq"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Hamısına baxın"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Şəbəkəni dəyişmək üçün etherneti ayırın"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> aşağıdakı mozaiki Sürətli Ayarlara əlavə etmək istəyir"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 435c890..8ec3417 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -1181,8 +1181,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Otključajte da biste videli mreže"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traže se mreže…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje sa mrežom nije uspelo"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi trenutno ne može da se automatski poveže"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Pogledajte sve"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste promenili mrežu, prekinite eternet vezu"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi da doda sledeću pločicu u Brza podešavanja"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 6a36507..42680ea 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -1187,8 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Разблакіраваць для прагляду сетак"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Выконваецца пошук сетак…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не ўдалося падключыцца да сеткі"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Аўтаматычнае падключэнне да Wi-Fi адсутнічае"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Паказаць усе"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Каб падключыцца да сетак, выключыце Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> запытвае дазвол на дадаванне ў хуткія налады наступнай пліткі"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 07b231a..6c1ce79 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Отключване с цел преглед на мрежите"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Търсят се мрежи…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Свързването с мрежата не бе успешно"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Засега Wi-Fi няма да се свързва автоматично"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Вижте всички"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За да превключите мрежите, прекъснете връзката с Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> иска да добави следния панел към бързите настройки"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 78b04c1..cf8c641 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"নেটওয়ার্ক দেখার জন্য আনলক করুন"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"নেটওয়ার্ক সার্চ করা হচ্ছে…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"নেটওয়ার্কে কানেক্ট করা যায়নি"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"এখন ওয়াই-ফাই নিজে থেকে কানেক্ট হবে না"</string>
<string name="see_all_networks" msgid="3773666844913168122">"সবকটি দেখুন"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"নেটওয়ার্ক বদলাতে ইথারনেট ডিসকানেক্ট করুন"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> নিম্নলিখিত টাইল দ্রুত সেটিংস মেনুতে যোগ করতে চায়"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index c03f8a9..64c911c 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloqueja per veure xarxes"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"S\'estan cercant xarxes…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"No s\'ha pogut connectar a la xarxa"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Per ara la Wi‑Fi no es connectarà automàticament"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Mostra-ho tot"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per canviar de xarxa, desconnecta la connexió Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vol afegir la icona següent a la configuració ràpida"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index a3284b7..47b5ff0 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -1187,8 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Sítě uvidíte po odemknutí"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhledávání sítí…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Připojení k síti se nezdařilo"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi se prozatím nebude připojovat automaticky"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Zobrazit vše"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pokud chcete přepnout sítě, odpojte ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikace <xliff:g id="APPNAME">%1$s</xliff:g> chce do Rychlého nastavení přidat následující dlaždici"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 499b2b8..59c24ce 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Lås op for at se netværk"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søger efter netværk…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Der kunne ikke oprettes forbindelse til netværket"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Ingen automatisk forbindelse til Wi-Fi i øjeblikket"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Afbryd ethernetforbindelsen for at skifte netværk"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vil gerne føje dette handlingsfelt til Kvikmenu"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 72ed84e..0bfe97e 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Entsperren, um Netzwerke anzuzeigen"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netzwerke werden gesucht…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Netzwerkverbindung konnte nicht hergestellt werden"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Zurzeit wird keine automatische WLAN-Verbindung hergestellt"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Alle ansehen"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Trenne das Ethernetkabel, um das Netzwerk zu wechseln"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> möchte die folgende Kachel den Schnelleinstellungen hinzufügen"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 1eb7da7..4b4371b 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Ξεκλειδώστε για προβολή δικτύων"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Αναζήτηση δικτύων…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Αποτυχία σύνδεσης στο δίκτυο"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Δεν θα γίνεται προς το παρόν αυτόματη σύνδεση Wi-Fi."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Εμφάνιση όλων"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Για εναλλαγή δικτύων, αποσυνδέστε το ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Η εφαρμογή <xliff:g id="APPNAME">%1$s</xliff:g> θέλει να προσθέσει το παρακάτω πλακίδιο στις Γρήγορες ρυθμίσεις"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 0e1e49a..2eebb52 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index bcfc47a..5ce2897 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 0e1e49a..2eebb52 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 0e1e49a..2eebb52 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Unlock to view networks"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Searching for networks…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Failed to connect to network"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi won’t auto-connect for now"</string>
<string name="see_all_networks" msgid="3773666844913168122">"See all"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"To switch networks, disconnect Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wants to add the following tile to Quick Settings"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 23f275a..ac93bf5 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloquea para ver redes"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"No se ha podido conectar a la red"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Por ahora no se conectará automáticamente a redes Wi-Fi"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de red, desconecta el cable Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> quiere añadir el siguiente recuadro a ajustes rápidos"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 9595838..ab33374 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Võrkude vaatamiseks avage"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Võrkude otsimine …"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Võrguühenduse loomine ebaõnnestus"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi-ühendust ei looda praegu automaatselt"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Kuva kõik"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Võrkude vahetamiseks katkestage Etherneti-ühendus"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> soovib kiirseadetesse lisada järgmise paani"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index a33e4e8..6731ed1 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -845,7 +845,7 @@
<string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"Arakatzailea"</string>
<string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"Kontaktuak"</string>
<string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"Posta"</string>
- <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS mezuak"</string>
+ <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMSak"</string>
<string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"Musika"</string>
<string name="keyboard_shortcut_group_applications_youtube" msgid="5078136084632450333">"YouTube"</string>
<string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 69d542a..69631d4 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"برای مشاهده شبکهها، قفل صفحه را باز کنید"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"درحال جستجوی شبکه…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"اتصال به شبکه برقرار نشد"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"فعلاً Wi-Fi بهطور خودکار متصل نمیشود"</string>
<string name="see_all_networks" msgid="3773666844913168122">"مشاهده همه"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"برای تغییر شبکه، اترنت را قطع کنید"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> میخواهد کاشی زیر را به «تنظیمات فوری» اضافه کند"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 121c955..eed2a6c 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Avaa lukitus nähdäksesi verkot"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Etsitään verkkoja…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yhteyden muodostaminen verkkoon epäonnistui"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ei toistaiseksi yhdistä automaattisesti"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Näytä kaikki"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Irrota Ethernet-johto, jos haluat vaihtaa verkkoa"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> haluaa lisätä seuraavan laatan pika-asetuksiin"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 2d43614..2187f77 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Déverrouillez l\'écran pour afficher les réseaux Wi-Fi"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux en cours…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connexion automatique au Wi-Fi impossible pour le moment"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, débranchez le câble Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"L\'application <xliff:g id="APPNAME">%1$s</xliff:g> veut ajouter la tuile suivante au menu Paramètres rapides"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index c10ae39..d927847 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Déverrouiller pour afficher les réseaux"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Recherche de réseaux…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Échec de la connexion au réseau"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connexion automatique au Wi-Fi désactivée pour le moment"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Tout afficher"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pour changer de réseau, déconnectez l\'Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> veut ajouter le bloc suivant aux Réglages rapides"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 4c7f71c..a7c1f50 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Desbloquea a pantalla para ver as redes"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Buscando redes…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Produciuse un erro ao conectarse á rede"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"De momento, a wifi non se conectará automaticamente"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Ver todo"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para cambiar de rede, desconecta a Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> solicita a túa aprobación para engadir o seguinte atallo a Configuración rápida"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index a0b3fc7..f63e583 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"વાઇ-ફાઇ નેટવર્ક જોવા માટે અનલૉક કરો"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"નેટવર્ક શોધી રહ્યાં છીએ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"નેટવર્ક સાથે કનેક્ટ કરવામાં નિષ્ફળ થયાં"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"હમણાં પૂરતું વાઇ-ફાઇ ઑટોમૅટિક રીતે કનેક્ટ થશે નહીં"</string>
<string name="see_all_networks" msgid="3773666844913168122">"બધા જુઓ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"બીજા નેટવર્ક પર જવા માટે, ઇથરનેટ ડિસ્કનેક્ટ કરો"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"ઝડપી સેટિંગમાં <xliff:g id="APPNAME">%1$s</xliff:g> નીચે જણાવેલા ટાઇલ ઉમેરવા માગે છે"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index df68e07..f586447 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -1181,8 +1181,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Otključajte za prikaz mreža"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Traženje mreža…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Povezivanje s mrežom nije uspjelo"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi se zasad neće automatski povezivati"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da biste se prebacili na drugu mrežu, odspojite Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi dodati sljedeću pločicu u Brze postavke"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index a2d7be1..202ab05 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Zárolás feloldása a hálózatok megtekintéséhez"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Hálózatok keresése…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nem sikerült hálózathoz csatlakozni."</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"A Wi-Fi-re történő csatlakozás jelenleg nem automatikus"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Megtekintés"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Hálózatváltáshoz válassza le az ethernetet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"A(z) <xliff:g id="APPNAME">%1$s</xliff:g> a következő mozaikot szeretné hozzáadni a Gyorsbeállításokhoz"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index c4e61af..d359409 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Ապակողպեք՝ ցանցերը դիտելու համար"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ցանցերի որոնում…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Չհաջողվեց միանալ ցանցին"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi-ն ավտոմատ չի միանա"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Տեսնել բոլորը"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Մի ցանցից մյուսին անցնելու համար անջատեք Ethernet-ը"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> հավելվածն ուզում է ավելացնել հետևյալ սալիկը Արագ կարգավորումներում"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index b3da8f6..947e8882 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Buka kunci untuk melihat jaringan"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari jaringan …"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menghubungkan ke jaringan"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tidak akan otomatis terhubung untuk saat ini"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk beralih jaringan, lepaskan kabel ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ingin menambahkan kartu berikut ke Setelan Cepat"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 22a43c7..a643b24 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Taktu úr lás til að skoða netkerfi"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Leitar að netum…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ekki tókst að tengjast neti"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tengist ekki sjálfkrafa eins og er"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Sjá allt"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aftengdu ethernet til að skipta um net"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vill bæta eftirfarandi reit við flýtistillingar"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 0fc23f7..bb77f0c 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Sblocca per visualizzare le reti"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ricerca di reti in corso…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Connessione alla rete non riuscita"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Connessione automatica rete Wi-Fi non attiva al momento"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Mostra tutte"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Per cambiare rete, scollega il cavo Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vuole aggiungere il seguente riquadro alle Impostazioni rapide"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 35236cd..90a2217 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -1187,8 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"צריך לבטל את הנעילה כדי להציג את הרשתות"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"בתהליך חיפוש רשתות…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"נכשל הניסיון להתחבר לרשת"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ה-Wi-Fi לא יתחבר באופן אוטומטי בינתיים"</string>
<string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"כדי לעבור בין רשתות, צריך לנתק את האתרנט"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"האפליקציה <xliff:g id="APPNAME">%1$s</xliff:g> מבקשת להוסיף להגדרות המהירות את האריח הבא"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 2d871ba..97bf7fb 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ネットワークを表示するにはロック解除してください"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ネットワークを検索しています…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ネットワークに接続できませんでした"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi に自動接続しません"</string>
<string name="see_all_networks" msgid="3773666844913168122">"すべて表示"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ネットワークを変更するにはイーサネット接続を解除してください"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> が以下のタイルをクイック設定に追加しようとしています"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 3df7a69d..e1c5b8d 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"განბლოკვა ქსელების სანახავად"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"მიმდინარეობს ქსელების ძიება…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ქსელთან დაკავშირება ვერ ხერხდება"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ინტერნეტს დროებით ავტომატურად არ დაუკავშირდება"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ყველას ნახვა"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ქსელების გადასართავად, გაწყვიტეთ Ethernet-თან კავშირი"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g>-ს სურს, დაამატოს შემდეგი მოზაიკა სწრაფ პარამეტრებში"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 824ec86..8462d00 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Желілерді көру үшін құлыпты ашыңыз."</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Маңайдағы желілер ізделуде…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Желіге қосылмады."</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Әзірше Wi-Fi автоматты түрде қосылмайды."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Барлығын көру"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Желілерді ауыстыру үшін ethernet кабелін ажыратыңыз."</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> Жылдам параметрлерге келесі бөлшекті қосқысы келеді."</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 9b69265..f882906 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ដោះសោដើម្បីមើលបណ្ដាញ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"កំពុងស្វែងរកបណ្ដាញ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"មិនអាចភ្ជាប់បណ្ដាញបានទេ"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិក្នុងពេលនេះទេ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ដើម្បីប្ដូរបណ្ដាញ សូមផ្ដាច់អ៊ីសឺរណិត"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ចង់បញ្ចូលប្រអប់ខាងក្រោមទៅក្នុងការកំណត់រហ័ស"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 2b07d2b..47fa557 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಅನ್ಲಾಕ್ ಮಾಡಿ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ಹುಡುಕಲಾಗುತ್ತಿದೆ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ನೆಟ್ವರ್ಕ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲು ವಿಫಲವಾಗಿದೆ"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ಸದ್ಯದ ಮಟ್ಟಿಗೆ ವೈ-ಫೈ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕನೆಕ್ಟ್ ಆಗುವುದಿಲ್ಲ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ನೆಟ್ವರ್ಕ್ಗಳನ್ನು ಬದಲಿಸಲು, ಇಥರ್ನೆಟ್ ಅನ್ನು ಡಿಸ್ಕನೆಕ್ಟ್ ಮಾಡಿ"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ಈ ಕೆಳಗಿನ ಟೈಲ್ ಅನ್ನು ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳಿಗೆ ಸೇರಿಸಲು ಬಯಸುತ್ತದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 1e1509c..d1d804a 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"네트워크를 보려면 잠금 해제하세요"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"네트워크 검색 중…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"네트워크에 연결하지 못했습니다."</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"지금은 Wi-Fi가 자동으로 연결되지 않습니다."</string>
<string name="see_all_networks" msgid="3773666844913168122">"모두 보기"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"네트워크를 전환하려면 이더넷을 연결 해제하세요."</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g>에서 빠른 설정에 다음 타일을 추가하려고 합니다."</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index cdfb768..d5bbb41 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Тармактарды көрүү үчүн кулпусун ачыңыз"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Тармактар изделүүдө…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Тармакка туташпай калды"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi азырынча автоматтык түрдө туташпайт"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Баарын көрүү"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Башка тармактарга которулуу үчүн Ethernet кабелин ажыратыңыз"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> төмөнкү ыкчам баскычты Ыкчам жөндөөлөргө кошкону жатат"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index a021bfb..cc6c51d 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ປົດລັອກເພື່ອເບິ່ງເຄືອຂ່າຍ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ກຳລັງຊອກຫາເຄືອຂ່າຍ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ເຊື່ອມຕໍ່ເຄືອຂ່າຍບໍ່ສຳເລັດ"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດສຳລັບຕອນນີ້"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ເບິ່ງທັງໝົດ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ເພື່ອສະຫຼັບເຄືອຂ່າຍ, ໃຫ້ຕັດການເຊື່ອມຕໍ່ອີເທີເນັດກ່ອນ"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ຕ້ອງການເພີ່ມແຜ່ນຕໍ່ໄປນີ້ໃສ່ການຕັ້ງຄ່າດ່ວນ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 6d57dafe..91711e0 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -1187,8 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Atrakinkite, kad peržiūrėtumėte visus tinklus"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ieškoma tinklų…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Jungiantis prie tinklo įvyko klaida"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"„Wi-Fi“ šiuo metu nebus prijungtas automatiškai"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Žiūrėti viską"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Norėdami perjungti tinklus, atjunkite eternetą"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"„<xliff:g id="APPNAME">%1$s</xliff:g>“ nori prie sparčiųjų nustatymų pridėti toliau pateiktą išklotinės elementą"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 0d2c375..f8611ce 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -1181,8 +1181,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Lai skatītu tīklus, atbloķējiet"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Notiek tīklu meklēšana…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Neizdevās izveidot savienojumu ar tīklu"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi savienojums īslaicīgi netiks izveidots automātiski."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Visu tīklu skatīšana"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Lai pārslēgtu tīklus, atvienojiet tīkla Ethernet vadu."</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> pieprasa atļauju pievienot tālāk norādīto elementu ātrajiem iestatījumiem"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index d63a2e6..f20b74e 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Отклучете за да се прикажат мрежите"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Се пребаруваат мрежи…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не успеа да се поврзе на мрежата"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi нема да се поврзува автоматски засега"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Прикажи ги сите"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"За промена на мрежата, прекинете ја врската со етернетот"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> сака да ја додаде следнава плочка на „Брзите поставки“"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 93af5c3..5ac1696 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"നെറ്റ്വർക്കുകൾ കാണാൻ അൺലോക്ക് ചെയ്യുക"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"നെറ്റ്വർക്കുകൾ തിരയുന്നു…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"നെറ്റ്വർക്കിൽ കണക്റ്റ് ചെയ്യാനായില്ല"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"വൈഫൈ ഇപ്പോൾ സ്വയമേവ കണക്റ്റ് ചെയ്യില്ല"</string>
<string name="see_all_networks" msgid="3773666844913168122">"എല്ലാം കാണുക"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"മറ്റ് നെറ്റ്വർക്കുകളിലേക്ക് മാറാൻ, ഇതർനെറ്റ് വിച്ഛേദിക്കുക"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"ദ്രുത ക്രമീകരണത്തിലേക്ക് ഇനിപ്പറയുന്ന ടൈൽ ചേർക്കാൻ <xliff:g id="APPNAME">%1$s</xliff:g> ആവശ്യപ്പെടുന്നു"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 0cae107..5d07534 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Сүлжээг харахын тулд түгжээг тайлах"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Сүлжээ хайж байна…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Сүлжээнд холбогдож чадсангүй"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi-г одоогоор автоматаар холбохгүй"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Бүгдийг харах"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Сүлжээг сэлгэхийн тулд этернэтийг салгана уу"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> нь дараах хавтанг Шуурхай тохиргоонд нэмэх хүсэлтэй байна"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 9450815..eb2d4c2 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"नेटवर्क पाहण्यासाठी स्क्रीन अनलॉक करा"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्क शोधत आहे…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कशी कनेक्ट करता आले नाही"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"सध्या वाय-फाय ऑटो-कनेक्ट होणार नाही"</string>
<string name="see_all_networks" msgid="3773666844913168122">"सर्व पहा"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क स्विच करण्यासाठी, इथरनेट केबल डिस्कनेक्ट करा"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ला क्विक सेटिंग्जमध्ये पुढील टाइल जोडायची आहे"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 0c45ea7..b9e583d 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Buka kunci untuk melihat rangkaian"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Mencari rangkaian…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Gagal menyambung kepada rangkaian"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi tidak akan disambungkan secara automatik buat masa ini"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Lihat semua"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Untuk menukar rangkaian, putuskan sambungan ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> mahu menambah jubin yang berikut kepada Tetapan Pantas"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 5d129c9e4..7f6eccc 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ကွန်ရက်များကြည့်ရန် ဖွင့်ပါ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ကွန်ရက်များကို ရှာဖွေနေသည်…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ကွန်ရက်သို့ ချိတ်ဆက်၍မရပါ"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi က လောလောဆယ် အလိုအလျောက် ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"အားလုံးကြည့်ရန်"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ကွန်ရက်ပြောင်းရန် အီသာနက်ကို ချိတ်ဆက်မှုဖြုတ်ပါ"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> က ‘အမြန် ဆက်တင်များ’ တွင် အောက်ပါအကွက်ငယ်ကို ထည့်လိုသည်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 5f4e7ca5..e5206c0 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Lås opp for å se nettverk"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Søker etter nettverk …"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kunne ikke koble til nettverket"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi kobles ikke til automatisk inntil videre"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Se alle"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"For å bytte nettverk, koble fra Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vil legge til denne brikken i Hurtiginnstillinger"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index b7ea6ac..be7a404c 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"नेटवर्कहरू हेर्न आफ्नो स्क्रिन अनलक गर्नुहोस्"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"नेटवर्कहरू खोजिँदै छन्…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"नेटवर्कमा कनेक्ट गर्न सकिएन"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"केही समयका लागि Wi-Fi स्वतः कनेक्ट हुँदैन"</string>
<string name="see_all_networks" msgid="3773666844913168122">"सबै नेटवर्क हेर्नुहोस्"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"नेटवर्क बदल्न इथरनेट डिस्कनेक्ट गर्नुहोस्"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> द्रुत सेटिङमा निम्न टाइल हाल्न चाहन्छ"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 0fd9227..ed2c45a 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Ontgrendel het scherm om netwerken te bekijken"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Netwerken zoeken…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Kan geen verbinding maken met het netwerk"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wifi maakt momenteel niet automatisch verbinding"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Alles tonen"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Verbreek de ethernetverbinding om van netwerk te wisselen"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wil de volgende tegel toevoegen aan Snelle instellingen"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 3379405..69650b2 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ନେଟୱାର୍କଗୁଡ଼ିକୁ ଦେଖିବା ପାଇଁ ଅନଲକ୍ କରନ୍ତୁ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ନେଟୱାର୍କଗୁଡ଼ିକ ସନ୍ଧାନ କରାଯାଉଛି…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ନେଟୱାର୍କକୁ ସଂଯୋଗ କରିବାରେ ବିଫଳ ହୋଇଛି"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ବର୍ତ୍ତମାନ ପାଇଁ ୱାଇ-ଫାଇ ସ୍ୱତଃ-ସଂଯୋଗ ହେବ ନାହିଁ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ସବୁ ଦେଖନ୍ତୁ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ନେଟୱାର୍କ ସ୍ୱିଚ୍ କରିବାକୁ, ଇଥରନେଟ୍ ବିଚ୍ଛିନ୍ନ କରନ୍ତୁ"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> କ୍ୱିକ୍ ସେଟିଂସରେ ନିମ୍ନୋକ୍ତ ଟାଇଲ୍ ଯୋଗ କରିବାକୁ ଚାହେଁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 2bfcc9e..29975a7 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ਨੈੱਟਵਰਕਾਂ ਨੂੰ ਦੇਖਣ ਲਈ ਅਣਲਾਕ ਕਰੋ"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ਨੈੱਟਵਰਕ ਖੋਜੇ ਜਾ ਰਹੇ ਹਨ…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ਫ਼ਿਲਹਾਲ ਵਾਈ-ਫਾਈ ਸਵੈ-ਕਨੈਕਟ ਨਹੀਂ ਹੋਵੇਗਾ"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ਸਭ ਦੇਖੋ"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ਨੈੱਟਵਰਕਾਂ ਨੂੰ ਬਦਲਣ ਲਈ, ਈਥਰਨੈੱਟ ਨੂੰ ਡਿਸਕਨੈਕਟ ਕਰੋ"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ਅੱਗੇ ਦਿੱਤੀ ਟਾਇਲ ਨੂੰ ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰਨਾ ਚਾਹੁੰਦੀ ਹੈ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 2dc8e6a..987def8 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -1187,8 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Odblokuj, by wyświetlić sieci"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Szukam sieci…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nie udało się połączyć z siecią"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi nie będzie na razie włączać się automatycznie"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Pokaż wszystko"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Aby przełączać sieci, odłącz Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikacja <xliff:g id="APPNAME">%1$s</xliff:g> chce dodać do Szybkich ustawień ten kafelek"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 59138ea..d6fc9c0 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -1181,8 +1181,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Deblocați pentru a vedea rețelele"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Se caută rețele…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nu s-a realizat conexiunea la rețea"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Deocamdată, Wi-Fi nu se poate conecta automat"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Afișează-le pe toate"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pentru a schimba rețeaua, deconectați ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vrea să adauge următorul card la Setări rapide"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 19f779c..74283cc 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -1187,8 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Разблокируйте, чтобы посмотреть сети Wi-Fi."</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Поиск сетей…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не удалось подключиться к сети"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Подключение по Wi-Fi не установится автоматически."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Показать все"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Чтобы переключиться между сетями, отключите кабель Ethernet."</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Приложение \"<xliff:g id="APPNAME">%1$s</xliff:g>\" хочет добавить в меню \"Быстрые настройки\" указанный параметр."</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 540ff08..33abc73 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ජාල බැලීමට අගුලු හරින්න"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"ජාල සඳහා සොයමින්…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"ජාලය වෙත සම්බන්ධ වීම අසාර්ථක විය"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi දැනට ස්වයං-සබැඳි නොවනු ඇත"</string>
<string name="see_all_networks" msgid="3773666844913168122">"සියල්ල බලන්න"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ජාල මාරු කිරීමට, ඊතර්නෙට් විසන්ධි කරන්න"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> හට ක්ෂණික සැකසීම් වෙත පහත ටයිල් එක් කිරීමට අවශ්යයි"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 8a6afdd..3c1ddae 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -1187,8 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Odomknutím si zobrazte siete"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Vyhľadávajú sa siete…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Nepodarilo sa pripojiť k sieti"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi‑Fi sa teraz automaticky nepripojí"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Zobraziť všetko"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ak chcete prepnúť siete, odpojte ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikácia <xliff:g id="APPNAME">%1$s</xliff:g> chce pridať do rýchlych nastavení túto kartu"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index fc2fe7c..7d60ade 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -1187,8 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Odklenite za ogled omrežij"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iskanje omrežij …"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Vzpostavljanje povezave z omrežjem ni uspelo."</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Vmesnik Wi-Fi trenutno ne bo samodejno vzpostavil povezave."</string>
<string name="see_all_networks" msgid="3773666844913168122">"Prikaz vseh omrežij"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Če želite preklopiti omrežje, prekinite ethernetno povezavo."</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Aplikacija <xliff:g id="APPNAME">%1$s</xliff:g> želi dodati to ploščico v hitre nastavitve."</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 0495d35..cc3fdbb 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Shkyçe për të parë rrjetet"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Po kërkon për rrjete…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Lidhja me rrjetin dështoi"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi nuk do të lidhet automatikisht për momentin"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Shiko të gjitha"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Për të ndërruar rrjetet, shkëput Ethernet-in"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> dëshiron të shtojë pllakëzën e mëposhtme te \"Cilësimet e shpejta\""</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 70567e1..5398dd7 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -1181,8 +1181,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Откључајте да бисте видели мреже"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Траже се мреже…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Повезивање са мрежом није успело"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi тренутно не може да се аутоматски повеже"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Погледајте све"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Да бисте променили мрежу, прекините етернет везу"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> жели да дода следећу плочицу у Брза подешавања"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 672e778..250f59b 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Lås upp för att visa nätverk"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Söker efter nätverk …"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Det gick inte att ansluta till nätverket"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Du ansluts inte till wifi automatiskt för närvarande"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Visa alla"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Koppla bort Ethernet för att växla nätverk"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> vill lägga till följande ruta i snabbinställningarna"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 97abfca..f4a999c 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -82,7 +82,7 @@
<string name="screenshot_saving_ticker" msgid="6519186952674544916">"Inahifadhi picha ya skrini..."</string>
<string name="screenshot_saving_title" msgid="2298349784913287333">"Inahifadhi picha ya skrini..."</string>
<string name="screenshot_saved_title" msgid="8893267638659083153">"Imehifadhi picha ya skrini"</string>
- <string name="screenshot_saved_text" msgid="7778833104901642442">"Gusa ili utazame picha ya skrini uliyohifadhi"</string>
+ <string name="screenshot_saved_text" msgid="7778833104901642442">"Gusa ili uone picha ya skrini uliyohifadhi"</string>
<string name="screenshot_failed_title" msgid="3259148215671936891">"Imeshindwa kuhifadhi picha ya skrini"</string>
<string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"Ni sharti ufungue kifaa kabla ya kuhifadhi picha ya skrini"</string>
<string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Jaribu kupiga picha ya skrini tena"</string>
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Fungua ili uangalie mitandao"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Inatafuta mitandao…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Imeshindwa kuunganisha kwenye mtandao"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi haitaunganishwa kiotomatiki kwa sasa"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Angalia yote"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ili kubadili mitandao, tenganisha ethaneti"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ingependa kuongeza kigae kifuatacho kwenye Mipangilio ya Haraka"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index d921d49..85f8f09 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -104,6 +104,6 @@
<!-- When split shade is used, this panel should be aligned to the top -->
<dimen name="qs_detail_margin_top">0dp</dimen>
- <!-- Internet panel related dimensions -->
- <dimen name="internet_dialog_list_max_width">624dp</dimen>
+ <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
+ <dimen name="large_dialog_width">624dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 43000fd..6738b8a 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"நெட்வொர்க்குகளைப் பார்க்க அன்லாக் செய்யுங்கள்"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"நெட்வொர்க்குகளைத் தேடுகிறது…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"நெட்வொர்க்குடன் இணைக்க முடியவில்லை"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"தற்போதைக்கு வைஃபை தானாக இணைக்கப்படாது"</string>
<string name="see_all_networks" msgid="3773666844913168122">"அனைத்தையும் காட்டு"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"நெட்வொர்க்குகளை மாற்ற ஈதர்நெட் இணைப்பைத் துண்டிக்கவும்"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"விரைவு அமைப்புகளில் பின்வரும் கட்டத்தைச் சேர்க்க <xliff:g id="APPNAME">%1$s</xliff:g> ஆப்ஸ் விரும்புகிறது"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index b63a3e8..cce52dd 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -101,7 +101,7 @@
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"స్క్రీన్ రికార్డింగ్ అవుతోంది"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"స్క్రీన్ రికార్డ్ సెషన్ కోసం ఆన్గోయింగ్ నోటిఫికేషన్"</string>
<string name="screenrecord_start_label" msgid="1750350278888217473">"రికార్డింగ్ను ప్రారంభించాలా?"</string>
- <string name="screenrecord_description" msgid="1123231719680353736">"రికార్డ్ చేస్తున్నప్పుడు, Android సిస్టమ్ మీ స్క్రీన్పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన ఏ సున్నితమైన సమాచారాన్నైనా క్యాప్చర్ చేయగలదు. ఈ సమాచారంలో, పాస్వర్డ్లు, పేమెంట్ వివరాలు, ఫోటోలు, మెసేజ్లు, ఆడియో ఉంటాయి."</string>
+ <string name="screenrecord_description" msgid="1123231719680353736">"రికార్డ్ చేస్తున్నప్పుడు, Android సిస్టమ్ మీ స్క్రీన్పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన ఏ సున్నితమైన సమాచారాన్నయినా క్యాప్చర్ చేయగలదు. ఈ సమాచారంలో, పాస్వర్డ్లు, పేమెంట్ వివరాలు, ఫోటోలు, మెసేజ్లు, ఆడియో కూడా ఉంటాయి."</string>
<string name="screenrecord_audio_label" msgid="6183558856175159629">"ఆడియోను రికార్డ్ చేయి"</string>
<string name="screenrecord_device_audio_label" msgid="9016927171280567791">"పరికరం ఆడియో"</string>
<string name="screenrecord_device_audio_description" msgid="4922694220572186193">"మీ పరికరం నుండి వచ్చే సంగీతం, కాల్స్, రింగ్టోన్ల వంటి ధ్వనులు"</string>
@@ -507,7 +507,7 @@
<string name="battery_saver_notification_title" msgid="8419266546034372562">"బ్యాటరీ సేవర్ ఆన్లో ఉంది"</string>
<string name="battery_saver_notification_text" msgid="2617841636449016951">"పనితీరుని మరియు నేపథ్య డేటాను తగ్గిస్తుంది"</string>
<string name="battery_saver_notification_action_text" msgid="6022091913807026887">"బ్యాటరీ సేవర్ను ఆఫ్ చేయండి"</string>
- <string name="media_projection_dialog_text" msgid="1755705274910034772">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు, మీ స్క్రీన్పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> యాక్సెస్ చేయగలుగుతుంది. ఈ సమాచారంలో, పాస్వర్డ్లు, చెల్లింపు వివరాలు, ఫోటోలు, మెసేజ్లు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string>
+ <string name="media_projection_dialog_text" msgid="1755705274910034772">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు, మీ స్క్రీన్పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> యాక్సెస్ చేయగలుగుతుంది. ఈ సమాచారంలో, పాస్వర్డ్లు, పేమెంట్ వివరాలు, ఫోటోలు, మెసేజ్లు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"రికార్డ్ చేస్తున్నప్పుడు లేదా ప్రసారం చేస్తున్నప్పుడు మీ స్క్రీన్పై ప్రదర్శించబడిన లేదా మీ పరికరం నుండి ప్లే చేయబడిన సమాచారం మొత్తాన్ని, ఈ ఫంక్షన్ను అందిస్తున్న సర్వీస్ యాక్సెస్ చేయగలదు. ఈ సమాచారంలో, పాస్వర్డ్లు, పేమెంట్ వివరాలు, ఫోటోలు, మెసేజ్లు, మీరు ప్లే చేసే ఆడియో వంటివి ఉంటాయి."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"రికార్డ్ చేయడం లేదా ప్రసారం చేయడం ప్రారంభించాలా?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>తో రికార్డ్ చేయడం లేదా ప్రసారం చేయడం ప్రారంభించాలా?"</string>
@@ -1145,7 +1145,7 @@
<string name="status_before_loading" msgid="1500477307859631381">"కంటెంట్ త్వరలో కనిపిస్తుంది"</string>
<string name="missed_call" msgid="4228016077700161689">"మిస్డ్ కాల్"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
- <string name="people_tile_description" msgid="8154966188085545556">"ఇటీవలి మెసేజ్లు, మిస్డ్ కాల్స్, అలాగే స్టేటస్ అప్డేట్లను చూడండి"</string>
+ <string name="people_tile_description" msgid="8154966188085545556">"ఇటీవలి మెసేజ్లు, మిస్స్డ్ కాల్స్, అలాగే స్టేటస్ అప్డేట్లను చూడండి"</string>
<string name="people_tile_title" msgid="6589377493334871272">"సంభాషణ"</string>
<string name="paused_by_dnd" msgid="7856941866433556428">"అంతరాయం కలిగించవద్దు ద్వారా పాజ్ చేయబడింది"</string>
<string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> మెసేజ్ను పంపారు: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"నెట్వర్క్లను చూడటానికి అన్లాక్ చేయండి"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"నెట్వర్క్ల కోసం సెర్చ్ చేస్తోంది…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"నెట్వర్క్కు కనెక్ట్ చేయడం విఫలమైంది"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ప్రస్తుతానికి Wi-Fi ఆటోమేటిక్గా కనెక్ట్ అవ్వదు"</string>
<string name="see_all_networks" msgid="3773666844913168122">"అన్నీ చూడండి"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"నెట్వర్క్లను మార్చడానికి, ఈథర్నెట్ను డిస్కనెక్ట్ చేయండి"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"కింది టైల్ను క్విక్ సెట్టింగ్లకు జోడించడానికి <xliff:g id="APPNAME">%1$s</xliff:g> అనుమతి కోరుతోంది"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index d49fe51..88ee383 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"ปลดล็อกเพื่อดูเครือข่าย"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"กำลังค้นหาเครือข่าย…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"เชื่อมต่อเครือข่ายไม่สำเร็จ"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi จะไม่เชื่อมต่ออัตโนมัติในตอนนี้"</string>
<string name="see_all_networks" msgid="3773666844913168122">"ดูทั้งหมด"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ตัดการเชื่อมต่ออีเทอร์เน็ตเพื่อสลับเครือข่าย"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ต้องการเพิ่มชิ้นส่วนต่อไปนี้ในการตั้งค่าด่วน"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 5f0a8f1..0338702 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"I-unlock para tingnan ang mga network"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Naghahanap ng mga network…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Hind nakakonekta sa network"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Hindi awtomatikong kokonekta ang Wi-Fi sa ngayon"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Tingnan lahat"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Para lumipat ng network, idiskonekta ang ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Gustong idagdag ng <xliff:g id="APPNAME">%1$s</xliff:g> ang sumusunod na tile sa Mga Mabilisang Setting"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 9200a8e..6017d9d 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Ağları görmek için kilidi açın"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Ağlar aranıyor…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Ağa bağlanılamadı"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Şu anda kablosuz ağa otomatik olarak bağlanılamıyor"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Tümünü göster"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ağ değiştirmek için ethernet bağlantısını kesin"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> aşağıdaki kartı Hızlı Ayarlar\'a eklemek istiyor"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index bd2fc40..82bf387 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -1187,8 +1187,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Розблокувати, щоб переглянути мережі"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Пошук мереж…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Не вдалося підключитися до мережі"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Пристрій не підключатиметься до Wi-Fi автоматично"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Показати все"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Щоб вибрати іншу мережу, від’єднайте кабель Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"Додаток <xliff:g id="APPNAME">%1$s</xliff:g> хоче додати такий параметр у меню швидких налаштувань:"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index f8e0785..f41b8aa 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"نیٹ ورکس کو دیکھنے کے لیے غیر مقفل کریں"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"نیٹ ورکس تلاش کیے جا رہے ہیں…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"نیٹ ورک سے منسلک ہونے میں ناکام ہو گیا"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ابھی Wi-Fi خود کار طور پر منسلک نہیں ہوگا"</string>
<string name="see_all_networks" msgid="3773666844913168122">"سبھی دیکھیں"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"نیٹ ورکس پر سوئچ کرنے کیلئے، ایتھرنیٹ غیر منسلک کریں"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> درج ذیل ٹائل کو فوری ترتیبات میں شامل کرنا چاہتی ہے"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 18e7824..4c73df2 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Mở khóa để xem mạng"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Đang tìm mạng…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Không kết nối được với mạng"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Tạm thời, Wi-Fi sẽ không tự động kết nối"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Xem tất cả"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Để chuyển mạng, hãy rút cáp Ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> muốn thêm ô bên dưới vào trình đơn Cài đặt nhanh"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index a297e24..376be92 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"解锁即可查看网络"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜索网络…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"未能连接到网络"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WLAN 暂时无法自动连接"</string>
<string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切换网络,请断开以太网连接"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"“<xliff:g id="APPNAME">%1$s</xliff:g>”希望将以下图块添加到“快捷设置”"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index f53acd2..11e6544 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"解鎖即可查看網絡"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網絡…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連接網絡"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"目前系統不會自動連線至 Wi-Fi"</string>
<string name="see_all_networks" msgid="3773666844913168122">"顯示全部"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網絡,請中斷以太網連線"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"「<xliff:g id="APPNAME">%1$s</xliff:g>」想在「快速設定」選單新增以下圖塊"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 2ba3a68..a82d751 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"解鎖螢幕即可查看網路"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"正在搜尋網路…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"無法連上網路"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"目前不會自動連上 Wi-Fi"</string>
<string name="see_all_networks" msgid="3773666844913168122">"查看全部"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"如要切換網路,請中斷乙太網路連線"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"「<xliff:g id="APPNAME">%1$s</xliff:g>」想在快速設定選單新增以下設定方塊"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 8c5d6de..03448d3 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -1175,8 +1175,7 @@
<string name="unlock_to_view_networks" msgid="5072880496312015676">"Vula ukuze ubuke amanethiwekhi"</string>
<string name="wifi_empty_list_wifi_on" msgid="3864376632067585377">"Iseshela amanethiwekhi…"</string>
<string name="wifi_failed_connect_message" msgid="4161863112079000071">"Yehlulekile ukuxhuma kunethiwekhi"</string>
- <!-- no translation found for wifi_wont_autoconnect_for_now (5782282612749867762) -->
- <skip />
+ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"I-Wi-Fi ngeke ixhume ngokuzenzakalelayo okwamanje"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Bona konke"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ukuze ushintshe amanethiwekhi, nqamula i-ethernet"</string>
<string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"I-<xliff:g id="APPNAME">%1$s</xliff:g> ifuna ukwengeza ithayela elilandelayo Kumasethingi Asheshayo"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 4b606bc..3cbbf97 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -70,7 +70,7 @@
<color name="keyguard_shadow_color">#B2000000</color>
<!-- Color for the images in keyguard number pad buttons -->
- <color name="keyguard_keypad_image_color">@android:color/background_light</color>
+ <color name="keyguard_keypad_image_color">?android:attr/textColorPrimaryInverse</color>
<!-- Color for rounded background for activated user in keyguard user switcher -->
<color name="kg_user_switcher_activated_background_color">#26000000</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 483dfc6..786b371 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -191,6 +191,15 @@
low powered state yet. -->
<bool name="doze_single_tap_uses_prox">true</bool>
+ <!-- Doze: whether the single tap sensor uses the proximity sensor in the given posture.
+ See doze_single_tap_uses_prox for usage. -->
+ <integer-array name="doze_single_tap_uses_prox_posture_mapping">
+ <item>1</item> <!-- UNKNOWN -->
+ <item>1</item> <!-- CLOSED -->
+ <item>1</item> <!-- HALF_OPENED -->
+ <item>1</item> <!-- OPENED -->
+ </integer-array>
+
<!-- Doze: whether the long press sensor uses the proximity sensor.
If both this parameter and doze_selectively_register_prox are true, registration for the
sensor will be delayed when the device first enters dozing but the device has not entered its
@@ -211,6 +220,16 @@
always-on display) -->
<string name="doze_brightness_sensor_type" translatable="false"></string>
+ <!-- Name of a sensor per posture state that provides a low-power estimate of the desired
+ display brightness, suitable to listen to while the device is asleep (e.g. during
+ always-on display) -->
+ <string-array name="doze_brightness_sensor_name_posture_mapping" translatable="false">
+ <item></item> <!-- UNKNOWN -->
+ <item></item> <!-- CLOSED -->
+ <item></item> <!-- HALF_OPENED -->
+ <item></item> <!-- OPENED -->
+ </string-array>
+
<!-- Override value to use for proximity sensor. -->
<string name="proximity_sensor_type" translatable="false"></string>
@@ -730,4 +749,8 @@
<item>@drawable/rounded_corner_bottom</item>
<item>@drawable/rounded_corner_bottom_secondary</item>
</array>
+
+ <!-- Flag to enable privacy dot views, it shall be true for normal case -->
+ <bool name="config_enablePrivacyDot">true</bool>
+
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2c439f5..8109e23 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1556,6 +1556,13 @@
<!-- Location on the screen of the center of the fingerprint sensor. For devices with under
display fingerprint sensors, this directly corresponds to the fingerprint sensor's location.
For devices with sensors on the back of the device, this corresponds to the location on the
+ screen directly in front of the sensor.
+ By default, this is set to @null to use the horizontal center of the screen. -->
+ <dimen name="physical_fingerprint_sensor_center_screen_location_x">@null</dimen>
+
+ <!-- Location on the screen of the center of the fingerprint sensor. For devices with under
+ display fingerprint sensors, this directly corresponds to the fingerprint sensor's location.
+ For devices with sensors on the back of the device, this corresponds to the location on the
screen directly in front of the sensor. -->
<dimen name="physical_fingerprint_sensor_center_screen_location_y">610px</dimen>
@@ -1593,7 +1600,9 @@
<!-- Internet panel related dimensions -->
<dimen name="internet_dialog_list_margin">12dp</dimen>
<dimen name="internet_dialog_list_max_height">646dp</dimen>
- <dimen name="internet_dialog_list_max_width">@dimen/match_parent</dimen>
+
+ <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
+ <dimen name="large_dialog_width">@dimen/match_parent</dimen>
<!-- Signal icon in internet dialog -->
<dimen name="signal_strength_icon_size">24dp</dimen>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 5aba333..6ca368c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -331,9 +331,6 @@
<style name="Animation.ShutdownUi" parent="@android:style/Animation.Toast">
</style>
- <style name="Animation.MediaOutputDialog" parent="@android:style/Animation.InputMethod">
- </style>
-
<!-- Standard animations for hiding and showing the status bar. -->
<style name="Animation.StatusBar">
</style>
@@ -436,10 +433,6 @@
<item name="android:windowCloseOnTouchOutside">true</item>
</style>
- <style name="Theme.SystemUI.Dialog.MediaOutput">
- <item name="android:windowBackground">@drawable/media_output_dialog_background</item>
- </style>
-
<style name="QSBorderlessButton">
<item name="android:padding">12dp</item>
<item name="android:background">@drawable/qs_btn_borderless_rect</item>
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
new file mode 100644
index 0000000..5c4f08e
--- /dev/null
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FeatureFlagManager.java
@@ -0,0 +1,43 @@
+/*
+ * 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.systemui.flags;
+import android.util.ArraySet;
+
+import com.android.systemui.dagger.SysUISingleton;
+
+/**
+ * Concrete implementation of the a Flag manager that returns default values for debug builds
+ */
+@SysUISingleton
+public class FeatureFlagManager {
+ public boolean isEnabled(int key, boolean defaultValue) {
+ return isEnabled(Integer.toString(key), defaultValue);
+ }
+
+ public boolean isEnabled(String key, boolean defaultValue) {
+ // TODO
+ return false;
+ }
+
+ public void setEnabled(int key, boolean value) {
+ setEnabled(Integer.toString(key), value);
+ }
+
+ public void setEnabled(String key, boolean value) {
+ // TODO
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 8f14cd8..f81f0b9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -11,6 +11,7 @@
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnPreDrawListener;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
@@ -93,6 +94,7 @@
private int[] mColorPalette;
private int mClockSwitchYAmount;
+ @VisibleForTesting boolean mChildrenAreLaidOut = false;
public KeyguardClockSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -284,11 +286,31 @@
if (mDisplayedClockSize != null && clockSize == mDisplayedClockSize) {
return false;
}
- animateClockChange(clockSize == LARGE);
- mDisplayedClockSize = clockSize;
+
+ // let's make sure clock is changed only after all views were laid out so we can
+ // translate them properly
+ if (mChildrenAreLaidOut) {
+ animateClockChange(clockSize == LARGE);
+ mDisplayedClockSize = clockSize;
+ } else {
+ getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ switchToClock(clockSize);
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ return true;
+ }
+ });
+ }
return true;
}
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ mChildrenAreLaidOut = true;
+ }
+
public Paint getPaint() {
return mClockView.getPaint();
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 539fb65..260b393 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -239,16 +239,6 @@
mView.setClockPlugin(null, mStatusBarStateController.getState());
mSmartspaceController.disconnect();
-
- // TODO: This is an unfortunate necessity since smartspace plugin retains a single instance
- // of the smartspace view -- if we don't remove the view, it can't be reused by a later
- // instance of this class. In order to fix this, we need to modify the plugin so that
- // (a) we get a new view each time and (b) we can properly clean up an old view by making
- // it unregister itself as a plugin listener.
- if (mSmartspaceView != null) {
- mView.removeView(mSmartspaceView);
- mSmartspaceView = null;
- }
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 92d1bc4..5969e92 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -90,6 +90,7 @@
import androidx.lifecycle.Observer;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
@@ -321,6 +322,7 @@
private boolean mIsDreaming;
private final DevicePolicyManager mDevicePolicyManager;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final InteractionJankMonitor mInteractionJankMonitor;
private boolean mLogoutEnabled;
// cached value to avoid IPCs
private boolean mIsUdfpsEnrolled;
@@ -1770,6 +1772,7 @@
AuthController authController,
TelephonyListenerManager telephonyListenerManager,
FeatureFlags featureFlags,
+ InteractionJankMonitor interactionJankMonitor,
@Nullable Vibrator vibrator) {
mContext = context;
mSubscriptionManager = SubscriptionManager.from(context);
@@ -1778,6 +1781,7 @@
mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged);
mBackgroundExecutor = backgroundExecutor;
mBroadcastDispatcher = broadcastDispatcher;
+ mInteractionJankMonitor = interactionJankMonitor;
mRingerModeTracker = ringerModeTracker;
mStatusBarStateController = statusBarStateController;
mStatusBarStateController.addCallback(mStatusBarStateControllerListener);
@@ -2637,7 +2641,8 @@
/**
* Handle {@link #MSG_USER_SWITCH_COMPLETE}
*/
- private void handleUserSwitchComplete(int userId) {
+ @VisibleForTesting
+ void handleUserSwitchComplete(int userId) {
Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -2645,6 +2650,7 @@
cb.onUserSwitchComplete(userId);
}
}
+ mInteractionJankMonitor.end(InteractionJankMonitor.CUJ_USER_SWITCH);
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
index c659bf7..f4ce643 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
+import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.graphics.drawable.VectorDrawable;
@@ -26,8 +27,6 @@
import androidx.annotation.Nullable;
-import com.android.systemui.R;
-
/**
* Similar to the {@link NumPadKey}, but displays an image.
*/
@@ -92,7 +91,10 @@
public void reloadColors() {
if (mAnimator != null) mAnimator.reloadColors(getContext());
- int imageColor = getContext().getColor(R.color.keyguard_keypad_image_color);
+ int[] customAttrs = {android.R.attr.textColorPrimaryInverse};
+ TypedArray a = getContext().obtainStyledAttributes(customAttrs);
+ int imageColor = a.getColor(0, 0);
+ a.recycle();
((VectorDrawable) getDrawable()).setTintList(ColorStateList.valueOf(imageColor));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 0ba183b..9c10d74 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -160,6 +160,7 @@
private Handler mHandler;
private boolean mPendingRotationChange;
private boolean mIsRoundedCornerMultipleRadius;
+ private boolean mIsPrivacyDotEnabled;
private int mStatusBarHeightPortrait;
private int mStatusBarHeightLandscape;
private Drawable mRoundedCornerDrawable;
@@ -253,6 +254,7 @@
mRotation = mContext.getDisplay().getRotation();
mDisplayUniqueId = mContext.getDisplay().getUniqueId();
mIsRoundedCornerMultipleRadius = isRoundedCornerMultipleRadius(mContext, mDisplayUniqueId);
+ mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot);
mWindowManager = mContext.getSystemService(WindowManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
updateRoundedCornerDrawable();
@@ -312,24 +314,24 @@
}
private void setupDecorations() {
- if (hasRoundedCorners() || shouldDrawCutout()) {
+ if (hasRoundedCorners() || shouldDrawCutout() || mIsPrivacyDotEnabled) {
updateStatusBarHeight();
final DisplayCutout cutout = getCutout();
- final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
- int rotatedPos;
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
- rotatedPos = getBoundPositionFromRotation(i, mRotation);
- if ((bounds != null && !bounds[rotatedPos].isEmpty())
- || shouldShowRoundedCorner(i)) {
- createOverlay(i);
+ if (shouldShowCutout(i, cutout) || shouldShowRoundedCorner(i, cutout)
+ || shouldShowPrivacyDot(i, cutout)) {
+ createOverlay(i, cutout);
} else {
removeOverlay(i);
}
}
- // Overlays have been created, send the dots to the controller
- //TODO: need a better way to do this
- mDotViewController.initialize(
- mTopLeftDot, mTopRightDot, mBottomLeftDot, mBottomRightDot);
+
+ if (mIsPrivacyDotEnabled) {
+ // Overlays have been created, send the dots to the controller
+ //TODO: need a better way to do this
+ mDotViewController.initialize(
+ mTopLeftDot, mTopRightDot, mBottomLeftDot, mBottomRightDot);
+ }
} else {
removeAllOverlays();
}
@@ -416,7 +418,7 @@
mOverlays[pos] = null;
}
- private void createOverlay(@BoundsPosition int pos) {
+ private void createOverlay(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
if (mOverlays == null) {
mOverlays = new View[BOUNDS_POSITION_LENGTH];
}
@@ -437,7 +439,7 @@
mOverlays[pos].setAlpha(0);
mOverlays[pos].setForceDarkAllowed(false);
- updateView(pos);
+ updateView(pos, cutout);
mWindowManager.addView(mOverlays[pos], getWindowLayoutParams(pos));
@@ -461,34 +463,19 @@
* Allow overrides for top/bottom positions
*/
private View overlayForPosition(@BoundsPosition int pos) {
- switch (pos) {
- case BOUNDS_POSITION_TOP:
- case BOUNDS_POSITION_LEFT:
- View top = LayoutInflater.from(mContext)
- .inflate(R.layout.rounded_corners_top, null);
- mTopLeftDot = top.findViewById(R.id.privacy_dot_left_container);
- mTopRightDot = top.findViewById(R.id.privacy_dot_right_container);
- return top;
- case BOUNDS_POSITION_BOTTOM:
- case BOUNDS_POSITION_RIGHT:
- View bottom = LayoutInflater.from(mContext)
- .inflate(R.layout.rounded_corners_bottom, null);
- mBottomLeftDot = bottom.findViewById(R.id.privacy_dot_left_container);
- mBottomRightDot = bottom.findViewById(R.id.privacy_dot_right_container);
- return bottom;
- default:
- throw new IllegalArgumentException("Unknown bounds position");
- }
+ final int layoutId = (pos == BOUNDS_POSITION_LEFT || pos == BOUNDS_POSITION_TOP)
+ ? R.layout.rounded_corners_top : R.layout.rounded_corners_bottom;
+ return LayoutInflater.from(mContext).inflate(layoutId, null);
}
- private void updateView(@BoundsPosition int pos) {
+ private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
if (mOverlays == null || mOverlays[pos] == null) {
return;
}
// update rounded corner view rotation
- updateRoundedCornerView(pos, R.id.left);
- updateRoundedCornerView(pos, R.id.right);
+ updateRoundedCornerView(pos, R.id.left, cutout);
+ updateRoundedCornerView(pos, R.id.right, cutout);
updateRoundedCornerSize(mRoundedDefault, mRoundedDefaultTop, mRoundedDefaultBottom);
updateRoundedCornerImageView();
@@ -496,6 +483,8 @@
if (mCutoutViews != null && mCutoutViews[pos] != null) {
mCutoutViews[pos].setRotation(mRotation);
}
+
+ updatePrivacyDotView(pos, cutout);
}
@VisibleForTesting
@@ -671,11 +660,12 @@
if (mOverlays != null) {
updateLayoutParams();
+ final DisplayCutout cutout = getCutout();
for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
if (mOverlays[i] == null) {
continue;
}
- updateView(i);
+ updateView(i, cutout);
}
}
}
@@ -808,13 +798,14 @@
return drawable;
}
- private void updateRoundedCornerView(@BoundsPosition int pos, int id) {
+ private void updateRoundedCornerView(@BoundsPosition int pos, int id,
+ @Nullable DisplayCutout cutout) {
final View rounded = mOverlays[pos].findViewById(id);
if (rounded == null) {
return;
}
rounded.setVisibility(View.GONE);
- if (shouldShowRoundedCorner(pos)) {
+ if (shouldShowRoundedCorner(pos, cutout)) {
final int gravity = getRoundedCornerGravity(pos, id == R.id.left);
((FrameLayout.LayoutParams) rounded.getLayoutParams()).gravity = gravity;
setRoundedCornerOrientation(rounded, gravity);
@@ -822,6 +813,26 @@
}
}
+ private void updatePrivacyDotView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
+ final ViewGroup viewGroup = (ViewGroup) mOverlays[pos];
+
+ final View left = viewGroup.findViewById(R.id.privacy_dot_left_container);
+ final View right = viewGroup.findViewById(R.id.privacy_dot_right_container);
+ if (shouldShowPrivacyDot(pos, cutout)) {
+ // TODO (b/201481944) Privacy Dots pos and var are wrong with long side cutout enable
+ if (pos == BOUNDS_POSITION_LEFT || pos == BOUNDS_POSITION_TOP) {
+ mTopLeftDot = left;
+ mTopRightDot = right;
+ } else {
+ mBottomLeftDot = left;
+ mBottomRightDot = right;
+ }
+ } else {
+ viewGroup.removeView(left);
+ viewGroup.removeView(right);
+ }
+ }
+
private int getRoundedCornerGravity(@BoundsPosition int pos, boolean isStart) {
final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
switch (rotatedPos) {
@@ -873,12 +884,8 @@
|| mIsRoundedCornerMultipleRadius;
}
- private boolean shouldShowRoundedCorner(@BoundsPosition int pos) {
- if (!hasRoundedCorners()) {
- return false;
- }
-
- DisplayCutout cutout = getCutout();
+ private boolean isDefaultShownOverlayPos(@BoundsPosition int pos,
+ @Nullable DisplayCutout cutout) {
// for cutout is null or cutout with only waterfall.
final boolean emptyBoundsOrWaterfall = cutout == null || cutout.isBoundsEmpty();
// Shows rounded corner on left and right overlays only when there is no top or bottom
@@ -893,6 +900,21 @@
}
}
+ private boolean shouldShowRoundedCorner(@BoundsPosition int pos,
+ @Nullable DisplayCutout cutout) {
+ return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout);
+ }
+
+ private boolean shouldShowPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
+ return mIsPrivacyDotEnabled && isDefaultShownOverlayPos(pos, cutout);
+ }
+
+ private boolean shouldShowCutout(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
+ final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
+ final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
+ return (bounds != null && !bounds[rotatedPos].isEmpty());
+ }
+
private boolean shouldDrawCutout() {
return shouldDrawCutout(mContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
index c32c1a2..48f6431 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
@@ -16,7 +16,6 @@
package com.android.systemui.biometrics;
-import android.annotation.NonNull;
import android.content.Context;
import android.graphics.drawable.Animatable2;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -30,6 +29,7 @@
import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
@@ -163,6 +163,18 @@
}
@Nullable @VisibleForTesting IconController mFaceIconController;
+ @NonNull private final OnAttachStateChangeListener mOnAttachStateChangeListener =
+ new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ mFaceIconController.deactivate();
+ }
+ };
public AuthBiometricFaceView(Context context) {
this(context, null);
@@ -181,6 +193,8 @@
protected void onFinishInflate() {
super.onFinishInflate();
mFaceIconController = new IconController(mContext, mIconView, mIndicatorView);
+
+ addOnAttachStateChangeListener(mOnAttachStateChangeListener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 71445a7..f4b446b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -29,6 +29,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.PointF;
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricConstants;
@@ -97,7 +98,7 @@
private final Provider<UdfpsController> mUdfpsControllerFactory;
private final Provider<SidefpsController> mSidefpsControllerFactory;
@Nullable private final PointF mFaceAuthSensorLocation;
- @Nullable private final PointF mFingerprintLocation;
+ @Nullable private PointF mFingerprintLocation;
private final Set<Callback> mCallbacks = new HashSet<>();
// TODO: These should just be saved from onSaveState
@@ -481,9 +482,7 @@
(float) faceAuthLocation[1]);
}
- mFingerprintLocation = new PointF(DisplayUtils.getWidth(mContext) / 2,
- mContext.getResources().getDimensionPixelSize(
- com.android.systemui.R.dimen.physical_fingerprint_sensor_center_screen_location_y));
+ updateFingerprintLocation();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -491,6 +490,21 @@
context.registerReceiver(mBroadcastReceiver, filter);
}
+ private void updateFingerprintLocation() {
+ int xLocation = DisplayUtils.getWidth(mContext) / 2;
+ try {
+ xLocation = mContext.getResources().getDimensionPixelSize(
+ com.android.systemui.R.dimen
+ .physical_fingerprint_sensor_center_screen_location_x);
+ } catch (Resources.NotFoundException e) {
+ }
+ int yLocation = mContext.getResources().getDimensionPixelSize(
+ com.android.systemui.R.dimen.physical_fingerprint_sensor_center_screen_location_y);
+ mFingerprintLocation = new PointF(
+ xLocation,
+ yLocation);
+ }
+
@SuppressWarnings("deprecation")
@Override
public void start() {
@@ -767,6 +781,7 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ updateFingerprintLocation();
// Save the state of the current dialog (buttons showing, etc)
if (mCurrentDialog != null) {
@@ -796,6 +811,7 @@
}
private void onOrientationChanged() {
+ updateFingerprintLocation();
if (mCurrentDialog != null) {
mCurrentDialog.onOrientationChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index ba64195..eb6b193 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -21,6 +21,7 @@
import android.content.res.Configuration
import android.graphics.PointF
import android.hardware.biometrics.BiometricSourceType
+import android.util.DisplayMetrics
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
@@ -45,6 +46,7 @@
import javax.inject.Inject
import javax.inject.Provider
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.util.leak.RotationUtils
private const val WAKE_AND_UNLOCK_FADE_DURATION = 180L
@@ -182,7 +184,7 @@
}
fun updateSensorLocation() {
- fingerprintSensorLocation = authController.fingerprintSensorLocation
+ updateFingerprintLocation()
faceSensorLocation = authController.faceAuthSensorLocation
fingerprintSensorLocation?.let {
circleReveal = CircleReveal(
@@ -197,6 +199,35 @@
}
}
+ private fun updateFingerprintLocation() {
+ val displayMetrics = DisplayMetrics()
+ sysuiContext.display?.getRealMetrics(displayMetrics)
+ val width = displayMetrics.widthPixels
+ val height = displayMetrics.heightPixels
+
+ authController.fingerprintSensorLocation?.let {
+ fingerprintSensorLocation = when (RotationUtils.getRotation(sysuiContext)) {
+ RotationUtils.ROTATION_LANDSCAPE -> {
+ val normalizedYPos: Float = it.y / width
+ val normalizedXPos: Float = it.x / height
+ PointF(width * normalizedYPos, height * (1 - normalizedXPos))
+ }
+ RotationUtils.ROTATION_UPSIDE_DOWN -> {
+ PointF(width - it.x, height - it.y)
+ }
+ RotationUtils.ROTATION_SEASCAPE -> {
+ val normalizedYPos: Float = it.y / width
+ val normalizedXPos: Float = it.x / height
+ PointF(width * (1 - normalizedYPos), height * normalizedXPos)
+ }
+ else -> {
+ // ROTATION_NONE
+ PointF(it.x, it.y)
+ }
+ }
+ }
+ }
+
private fun updateRippleColor() {
mView.setColor(
Utils.getColorAttr(sysuiContext, android.R.attr.colorAccent).defaultColor)
@@ -314,10 +345,12 @@
}
}
"fingerprint" -> {
+ updateSensorLocation()
pw.println("fingerprint ripple sensorLocation=$fingerprintSensorLocation")
showRipple(BiometricSourceType.FINGERPRINT)
}
"face" -> {
+ updateSensorLocation()
pw.println("face ripple sensorLocation=$faceSensorLocation")
showRipple(BiometricSourceType.FACE)
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 2d873f2..e117405 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -63,11 +63,11 @@
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.theme.ThemeOverlayApplier;
import com.android.systemui.unfold.UnfoldTransitionFactory;
import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index b210a19..d74df37 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -74,6 +74,7 @@
import com.android.internal.app.IBatteryStats;
import com.android.internal.appwidget.IAppWidgetService;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.LatencyTracker;
import com.android.systemui.dagger.qualifiers.DisplayId;
@@ -220,6 +221,12 @@
@Provides
@Singleton
+ static InteractionJankMonitor provideInteractionJankMonitor() {
+ return InteractionJankMonitor.getInstance();
+ }
+
+ @Provides
+ @Singleton
static InputMethodManager provideInputMethodManager(Context context) {
return context.getSystemService(InputMethodManager.class);
}
@@ -367,12 +374,6 @@
@Provides
@Singleton
- static Optional<TelecomManager> provideOptionalTelecomManager(Context context) {
- return Optional.ofNullable(context.getSystemService(TelecomManager.class));
- }
-
- @Provides
- @Singleton
static TelephonyManager provideTelephonyManager(Context context) {
return context.getSystemService(TelephonyManager.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 55e6154..3cefce8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -47,6 +47,7 @@
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.SecureSettings;
@@ -82,6 +83,9 @@
private boolean mListeningTouchScreenSensors;
private boolean mListeningProxSensors;
+ @DevicePostureController.DevicePostureInt
+ private int mDevicePosture;
+
// whether to only register sensors that use prox when the display state is dozing or off
private boolean mSelectivelyRegisterProxSensors;
@@ -106,7 +110,8 @@
DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog,
ProximitySensor proximitySensor, SecureSettings secureSettings,
- AuthController authController) {
+ AuthController authController,
+ int devicePosture) {
mContext = context;
mSensorManager = sensorManager;
mConfig = config;
@@ -120,6 +125,7 @@
mListeningProxSensors = !mSelectivelyRegisterProxSensors;
mScreenOffUdfpsEnabled =
config.screenOffUdfpsEnabled(KeyguardUpdateMonitor.getCurrentUser());
+ mDevicePosture = devicePosture;
boolean udfpsEnrolled =
authController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser());
@@ -142,7 +148,7 @@
false /* requires prox */,
dozeLog),
new TriggerSensor(
- findSensorWithType(config.doubleTapSensorType()),
+ findSensor(config.doubleTapSensorType()),
Settings.Secure.DOZE_DOUBLE_TAP_GESTURE,
true /* configured */,
DozeLog.REASON_SENSOR_DOUBLE_TAP,
@@ -150,7 +156,7 @@
true /* touchscreen */,
dozeLog),
new TriggerSensor(
- findSensorWithType(config.tapSensorType()),
+ findSensor(config.tapSensorType(mDevicePosture)),
Settings.Secure.DOZE_TAP_SCREEN_GESTURE,
true /* settingDef */,
true /* configured */,
@@ -158,10 +164,10 @@
false /* reports touch coordinates */,
true /* touchscreen */,
false /* ignoresSetting */,
- dozeParameters.singleTapUsesProx() /* requiresProx */,
+ dozeParameters.singleTapUsesProx(mDevicePosture) /* requiresProx */,
dozeLog),
new TriggerSensor(
- findSensorWithType(config.longPressSensorType()),
+ findSensor(config.longPressSensorType()),
Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
false /* settingDef */,
true /* configured */,
@@ -172,7 +178,7 @@
dozeParameters.longPressUsesProx() /* requiresProx */,
dozeLog),
new TriggerSensor(
- findSensorWithType(config.udfpsLongPressSensorType()),
+ findSensor(config.udfpsLongPressSensorType()),
"doze_pulse_on_auth",
true /* settingDef */,
udfpsEnrolled && (alwaysOn || mScreenOffUdfpsEnabled),
@@ -200,7 +206,7 @@
mConfig.getWakeLockScreenDebounce(),
dozeLog),
new TriggerSensor(
- findSensorWithType(config.quickPickupSensorType()),
+ findSensor(config.quickPickupSensorType()),
Settings.Secure.DOZE_QUICK_PICKUP_GESTURE,
true /* setting default */,
config.quickPickupSensorEnabled(KeyguardUpdateMonitor.getCurrentUser())
@@ -238,21 +244,29 @@
mDebounceFrom = SystemClock.uptimeMillis();
}
- private Sensor findSensorWithType(String type) {
- return findSensorWithType(mSensorManager, type);
+ private Sensor findSensor(String type) {
+ return findSensor(mSensorManager, type, null);
}
/**
- * Utility method to find a {@link Sensor} for the supplied string type.
+ * Utility method to find a {@link Sensor} for the supplied string type and string name.
+ *
+ * Return the first sensor in the list that matches the specified inputs. Ignores type or name
+ * if the input is null or empty.
+ *
+ * @param type sensorType
+ * @parm name sensorName, to differentiate between sensors with the same type
*/
- public static Sensor findSensorWithType(SensorManager sensorManager, String type) {
- if (TextUtils.isEmpty(type)) {
- return null;
- }
- List<Sensor> sensorList = sensorManager.getSensorList(Sensor.TYPE_ALL);
- for (Sensor s : sensorList) {
- if (type.equals(s.getStringType())) {
- return s;
+ public static Sensor findSensor(SensorManager sensorManager, String type, String name) {
+ final boolean isNameSpecified = !TextUtils.isEmpty(name);
+ final boolean isTypeSpecified = !TextUtils.isEmpty(type);
+ if (isNameSpecified || isTypeSpecified) {
+ final List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
+ for (Sensor sensor : sensors) {
+ if ((!isNameSpecified || name.equals(sensor.getName()))
+ && (!isTypeSpecified || type.equals(sensor.getStringType()))) {
+ return sensor;
+ }
}
}
return null;
@@ -370,6 +384,8 @@
/** Dump current state */
public void dump(PrintWriter pw) {
pw.println("mListening=" + mListening);
+ pw.println("mDevicePosture="
+ + DevicePostureController.devicePostureToString(mDevicePosture));
pw.println("mListeningTouchScreenSensors=" + mListeningTouchScreenSensors);
pw.println("mSelectivelyRegisterProxSensors=" + mSelectivelyRegisterProxSensors);
pw.println("mListeningProxSensors=" + mListeningProxSensors);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 756adca..1e7f0d9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -41,6 +41,7 @@
import com.android.systemui.doze.DozeMachine.State;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.Assert;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -95,6 +96,9 @@
private final DelayableExecutor mMainExecutor;
private final KeyguardStateController mKeyguardStateController;
private final UiEventLogger mUiEventLogger;
+ private final DevicePostureController mDevicePostureController;
+
+ private @DevicePostureController.DevicePostureInt int mDevicePosture;
private long mNotificationPulseTime;
private boolean mPulsePending;
@@ -182,7 +186,8 @@
SecureSettings secureSettings, AuthController authController,
@Main DelayableExecutor mainExecutor,
UiEventLogger uiEventLogger,
- KeyguardStateController keyguardStateController) {
+ KeyguardStateController keyguardStateController,
+ DevicePostureController devicePostureController) {
mContext = context;
mDozeHost = dozeHost;
mConfig = config;
@@ -190,9 +195,11 @@
mSensorManager = sensorManager;
mWakeLock = wakeLock;
mAllowPulseTriggers = true;
+ mDevicePostureController = devicePostureController;
+ mDevicePosture = devicePostureController.getDevicePosture();
mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters,
config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor,
- secureSettings, authController);
+ secureSettings, authController, mDevicePosture);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
mProxCheck = proxCheck;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
index 9c6e02a..571b666 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
@@ -38,6 +38,7 @@
import com.android.systemui.doze.DozeUi;
import com.android.systemui.doze.DozeWallpaperState;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -94,8 +95,15 @@
@Provides
@BrightnessSensor
static Optional<Sensor> providesBrightnessSensor(
- AsyncSensorManager sensorManager, Context context) {
- return Optional.ofNullable(DozeSensors.findSensorWithType(sensorManager,
- context.getString(R.string.doze_brightness_sensor_type)));
+ AsyncSensorManager sensorManager,
+ Context context,
+ DozeParameters dozeParameters,
+ DevicePostureController devicePostureController) {
+ return Optional.ofNullable(
+ DozeSensors.findSensor(
+ sensorManager,
+ context.getString(R.string.doze_brightness_sensor_type),
+ dozeParameters.brightnessName(devicePostureController.getDevicePosture())
+ ));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagManager.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagManager.java
new file mode 100644
index 0000000..dc09f2a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagManager.java
@@ -0,0 +1,36 @@
+/*
+ * 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.systemui.flags;
+
+import com.android.systemui.dagger.SysUISingleton;
+
+/**
+ * Default implementation of the a Flag manager that returns default values for release builds
+ */
+@SysUISingleton
+public class FeatureFlagManager {
+ public boolean getBoolean(int key, boolean defaultValue) {
+ return defaultValue;
+ }
+ public void setBoolean(int key, boolean value) {}
+ public boolean getBoolean(String key, boolean defaultValue) {
+ return defaultValue;
+ }
+ public void setBoolean(String key, boolean value) {}
+ public void addFlagChangedListener(Runnable run) {}
+ public void removeFlagUpdatedListener(Runnable run) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index e7445f9..424f801 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -364,9 +364,9 @@
seamlessView.setVisibility(View.VISIBLE);
setVisibleAndAlpha(collapsedSet, R.id.media_seamless, true /*visible */);
setVisibleAndAlpha(expandedSet, R.id.media_seamless, true /*visible */);
- seamlessView.setOnClickListener(v -> {
- mMediaOutputDialogFactory.create(data.getPackageName(), true);
- });
+ seamlessView.setOnClickListener(
+ v -> mMediaOutputDialogFactory.create(data.getPackageName(), true,
+ mPlayerViewHolder.getSeamlessButton()));
ImageView iconView = mPlayerViewHolder.getSeamlessIcon();
TextView deviceName = mPlayerViewHolder.getSeamlessText();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index eacdab6..06a1eea 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -889,7 +889,7 @@
dismissIntent = target
.baseAction
.extras
- .getParcelable(EXTRAS_SMARTSPACE_DISMISS_INTENT_KEY) as Intent
+ .getParcelable(EXTRAS_SMARTSPACE_DISMISS_INTENT_KEY) as Intent?
}
packageName(target)?.let {
return SmartspaceMediaData(target.smartspaceTargetId, isActive, true, it,
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
index 35603b6..f32dad6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
@@ -43,6 +43,7 @@
val seamless = itemView.requireViewById<ViewGroup>(R.id.media_seamless)
val seamlessIcon = itemView.requireViewById<ImageView>(R.id.media_seamless_image)
val seamlessText = itemView.requireViewById<TextView>(R.id.media_seamless_text)
+ val seamlessButton = itemView.requireViewById<View>(R.id.media_seamless_button)
// Seek bar
val seekBar = itemView.requireViewById<SeekBar>(R.id.media_progress_bar)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 391dff63..d1b6548 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -45,11 +45,14 @@
private static final String TAG = "MediaOutputAdapter";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private final MediaOutputDialog mMediaOutputDialog;
private ViewGroup mConnectedItem;
private boolean mIncludeDynamicGroup;
- public MediaOutputAdapter(MediaOutputController controller) {
+ public MediaOutputAdapter(MediaOutputController controller,
+ MediaOutputDialog mediaOutputDialog) {
super(controller);
+ mMediaOutputDialog = mediaOutputDialog;
}
@Override
@@ -136,7 +139,7 @@
mDivider.setTransitionAlpha(1);
mAddIcon.setVisibility(View.VISIBLE);
mAddIcon.setTransitionAlpha(1);
- mAddIcon.setOnClickListener(v -> onEndItemClick());
+ mAddIcon.setOnClickListener(this::onEndItemClick);
} else {
// Init non-active device layout
mDivider.setVisibility(View.GONE);
@@ -197,7 +200,7 @@
mDivider.setTransitionAlpha(1);
mAddIcon.setVisibility(View.VISIBLE);
mAddIcon.setTransitionAlpha(1);
- mAddIcon.setOnClickListener(v -> onEndItemClick());
+ mAddIcon.setOnClickListener(this::onEndItemClick);
} else {
mDivider.setVisibility(View.GONE);
mAddIcon.setVisibility(View.GONE);
@@ -232,8 +235,8 @@
}
}
- private void onEndItemClick() {
- mController.launchMediaOutputGroupDialog();
+ private void onEndItemClick(View view) {
+ mController.launchMediaOutputGroupDialog(mMediaOutputDialog.getDialogView());
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index cdcdf9a..85d0802 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -82,7 +82,7 @@
};
public MediaOutputBaseDialog(Context context, MediaOutputController mediaOutputController) {
- super(context, R.style.Theme_SystemUI_Dialog_MediaOutput);
+ super(context);
mContext = context;
mMediaOutputController = mediaOutputController;
mLayoutManager = new LinearLayoutManager(mContext);
@@ -97,15 +97,15 @@
mDialogView = LayoutInflater.from(mContext).inflate(R.layout.media_output_dialog, null);
final Window window = getWindow();
final WindowManager.LayoutParams lp = window.getAttributes();
- lp.gravity = Gravity.BOTTOM;
+ lp.gravity = Gravity.CENTER;
// Config insets to make sure the layout is above the navigation bar
lp.setFitInsetsTypes(statusBars() | navigationBars());
lp.setFitInsetsSides(WindowInsets.Side.all());
lp.setFitInsetsIgnoringVisibility(true);
window.setAttributes(lp);
window.setContentView(mDialogView);
- window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- window.setWindowAnimations(R.style.Animation_MediaOutputDialog);
+ window.setLayout(mContext.getResources().getDimensionPixelSize(R.dimen.large_dialog_width),
+ ViewGroup.LayoutParams.WRAP_CONTENT);
mHeaderTitle = mDialogView.requireViewById(R.id.header_title);
mHeaderSubtitle = mDialogView.requireViewById(R.id.header_subtitle);
@@ -229,4 +229,8 @@
void onHeaderIconClick() {
}
+
+ View getDialogView() {
+ return mDialogView;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index b2def7a..437a0c8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -32,6 +32,7 @@
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
+import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -48,6 +49,7 @@
import com.android.settingslib.media.MediaOutputConstants;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -73,6 +75,7 @@
private final MediaSessionManager mMediaSessionManager;
private final ShadeController mShadeController;
private final ActivityStarter mActivityStarter;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
private final boolean mAboveStatusbar;
private final NotificationEntryManager mNotificationEntryManager;
@@ -82,6 +85,7 @@
private MediaController mMediaController;
@VisibleForTesting
Callback mCallback;
+ Callback mPreviousCallback;
@VisibleForTesting
LocalMediaManager mLocalMediaManager;
@@ -92,7 +96,8 @@
public MediaOutputController(@NonNull Context context, String packageName,
boolean aboveStatusbar, MediaSessionManager mediaSessionManager, LocalBluetoothManager
lbm, ShadeController shadeController, ActivityStarter starter,
- NotificationEntryManager notificationEntryManager, UiEventLogger uiEventLogger) {
+ NotificationEntryManager notificationEntryManager, UiEventLogger uiEventLogger,
+ DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
mPackageName = packageName;
mMediaSessionManager = mediaSessionManager;
@@ -104,6 +109,7 @@
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
mUiEventLogger = uiEventLogger;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
}
void start(@NonNull Callback cb) {
@@ -129,7 +135,19 @@
}
return;
}
+
+ if (mPreviousCallback != null) {
+ Log.w(TAG,
+ "Callback started when mPreviousCallback is not null, which is unexpected");
+ mPreviousCallback.dismissDialog();
+ }
+
+ // If we start the output group dialog when the output dialog is shown, we need to keep a
+ // reference to the output dialog to set it back as the callback once we dismiss the output
+ // group dialog.
+ mPreviousCallback = mCallback;
mCallback = cb;
+
mLocalMediaManager.unregisterCallback(this);
mLocalMediaManager.stopScan();
mLocalMediaManager.registerCallback(this);
@@ -145,6 +163,15 @@
mLocalMediaManager.stopScan();
}
mMediaDevices.clear();
+
+ // If there was a previous callback, i.e. we just dismissed the output group dialog and are
+ // now back on the output dialog, then we reset the callback to its previous value.
+ mCallback = null;
+ Callback previous = mPreviousCallback;
+ mPreviousCallback = null;
+ if (previous != null) {
+ start(previous);
+ }
}
@Override
@@ -436,6 +463,10 @@
}
void launchBluetoothPairing() {
+ // Dismissing a dialog into its touch surface and starting an activity at the same time
+ // looks bad, so let's make sure the dialog just fades out quickly.
+ mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
+
mCallback.dismissDialog();
final ActivityStarter.OnDismissAction postKeyguardAction = () -> {
mContext.sendBroadcast(new Intent()
@@ -447,14 +478,10 @@
mActivityStarter.dismissKeyguardThenExecute(postKeyguardAction, null, true);
}
- void launchMediaOutputDialog() {
- mCallback.dismissDialog();
- new MediaOutputDialog(mContext, mAboveStatusbar, this, mUiEventLogger);
- }
-
- void launchMediaOutputGroupDialog() {
- mCallback.dismissDialog();
- new MediaOutputGroupDialog(mContext, mAboveStatusbar, this);
+ void launchMediaOutputGroupDialog(View mediaOutputDialog) {
+ // We show the output group dialog from the output dialog.
+ MediaOutputGroupDialog dialog = new MediaOutputGroupDialog(mContext, mAboveStatusbar, this);
+ mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog);
}
boolean isActiveRemoteDevice(@NonNull MediaDevice device) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index 53029bd0..eca8ac9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -40,11 +40,10 @@
mediaOutputController, UiEventLogger uiEventLogger) {
super(context, mediaOutputController);
mUiEventLogger = uiEventLogger;
- mAdapter = new MediaOutputAdapter(mMediaOutputController);
+ mAdapter = new MediaOutputAdapter(mMediaOutputController, this);
if (!aboveStatusbar) {
getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
}
- show();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index 0f340a5..b91901d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -18,8 +18,10 @@
import android.content.Context
import android.media.session.MediaSessionManager
+import android.view.View
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.LocalBluetoothManager
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.phone.ShadeController
@@ -35,19 +37,29 @@
private val shadeController: ShadeController,
private val starter: ActivityStarter,
private val notificationEntryManager: NotificationEntryManager,
- private val uiEventLogger: UiEventLogger
+ private val uiEventLogger: UiEventLogger,
+ private val dialogLaunchAnimator: DialogLaunchAnimator
) {
companion object {
var mediaOutputDialog: MediaOutputDialog? = null
}
/** Creates a [MediaOutputDialog] for the given package. */
- fun create(packageName: String, aboveStatusBar: Boolean) {
+ fun create(packageName: String, aboveStatusBar: Boolean, view: View? = null) {
+ // Dismiss the previous dialog, if any.
mediaOutputDialog?.dismiss()
- mediaOutputDialog = MediaOutputController(context, packageName, aboveStatusBar,
- mediaSessionManager, lbm, shadeController, starter, notificationEntryManager,
- uiEventLogger).run {
- MediaOutputDialog(context, aboveStatusBar, this, uiEventLogger)
+
+ val controller = MediaOutputController(context, packageName, aboveStatusBar,
+ mediaSessionManager, lbm, shadeController, starter, notificationEntryManager,
+ uiEventLogger, dialogLaunchAnimator)
+ val dialog = MediaOutputDialog(context, aboveStatusBar, controller, uiEventLogger)
+ mediaOutputDialog = dialog
+
+ // Show the dialog.
+ if (view != null) {
+ dialogLaunchAnimator.showFromView(dialog, view)
+ } else {
+ dialog.show()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
index 4079304..1300400 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
@@ -38,7 +38,6 @@
if (!aboveStatusbar) {
getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
}
- show();
}
@Override
@@ -83,6 +82,8 @@
@Override
void onHeaderIconClick() {
- mMediaOutputController.launchMediaOutputDialog();
+ // Given that we launched the media output group dialog from the media output dialog,
+ // dismissing this dialog will show the media output dialog again.
+ dismiss();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index d3a664b..8576a28 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -93,6 +93,7 @@
import android.view.Display;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
+import android.view.IWindowManager;
import android.view.InsetsState.InternalInsetsType;
import android.view.InsetsVisibilities;
import android.view.KeyEvent;
@@ -117,17 +118,20 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
import com.android.internal.view.AppearanceRegion;
+import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
import com.android.systemui.navigationbar.buttons.KeyButtonView;
import com.android.systemui.navigationbar.buttons.RotationContextButton;
import com.android.systemui.navigationbar.gestural.QuickswitchOrientedNavHandle;
+import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
@@ -147,6 +151,8 @@
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.pip.Pip;
@@ -156,8 +162,6 @@
import java.util.Optional;
import java.util.function.Consumer;
-import javax.inject.Inject;
-
import dagger.Lazy;
/**
@@ -239,13 +243,7 @@
private boolean mTransientShown;
private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
private LightBarController mLightBarController;
- private final LightBarController mMainLightBarController;
- private final LightBarController.Factory mLightBarControllerFactory;
private AutoHideController mAutoHideController;
- private final AutoHideController mMainAutoHideController;
- private final AutoHideController.Factory mAutoHideControllerFactory;
- private final Optional<TelecomManager> mTelecomManagerOptional;
- private final InputMethodManager mInputMethodManager;
@VisibleForTesting
public int mDisplayId;
@@ -269,7 +267,6 @@
private ViewTreeObserver.OnGlobalLayoutListener mOrientationHandleGlobalLayoutListener;
private boolean mShowOrientedHandleForImmersiveMode;
-
@com.android.internal.annotations.VisibleForTesting
public enum NavBarActionEvent implements UiEventLogger.UiEventEnum {
@@ -481,10 +478,11 @@
}
};
- private NavigationBar(Context context,
+ public NavigationBar(Context context,
WindowManager windowManager,
Lazy<AssistManager> assistManagerLazy,
AccessibilityManager accessibilityManager,
+ AccessibilityManagerWrapper accessibilityManagerWrapper,
DeviceProvisionedController deviceProvisionedController,
MetricsLogger metricsLogger,
OverviewProxyService overviewProxyService,
@@ -506,13 +504,7 @@
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
NavigationBarA11yHelper navigationBarA11yHelper,
- UserTracker userTracker,
- LightBarController mainLightBarController,
- LightBarController.Factory lightBarControllerFactory,
- AutoHideController mainAutoHideController,
- AutoHideController.Factory autoHideControllerFactory,
- Optional<TelecomManager> telecomManagerOptional,
- InputMethodManager inputMethodManager) {
+ UserTracker userTracker) {
mContext = context;
mWindowManager = windowManager;
mAccessibilityManager = accessibilityManager;
@@ -539,12 +531,6 @@
mNavigationBarA11yHelper = navigationBarA11yHelper;
mUserTracker = userTracker;
mNotificationShadeDepthController = notificationShadeDepthController;
- mMainLightBarController = mainLightBarController;
- mLightBarControllerFactory = lightBarControllerFactory;
- mMainAutoHideController = mainAutoHideController;
- mAutoHideControllerFactory = autoHideControllerFactory;
- mTelecomManagerOptional = telecomManagerOptional;
- mInputMethodManager = inputMethodManager;
mNavBarMode = mNavigationModeController.addListener(this);
}
@@ -562,7 +548,7 @@
mNavigationBarView = barView.findViewById(R.id.navigation_bar_view);
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView);
- mWindowManager.addView(mFrame,
+ mContext.getSystemService(WindowManager.class).addView(mFrame,
getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
.getRotation()));
mDisplayId = mContext.getDisplayId();
@@ -620,7 +606,8 @@
public void destroyView() {
setAutoHideController(/* autoHideController */ null);
mCommandQueue.removeCallback(this);
- mWindowManager.removeViewImmediate(mNavigationBarView.getRootView());
+ mContext.getSystemService(WindowManager.class).removeViewImmediate(
+ mNavigationBarView.getRootView());
mNavigationModeController.removeListener(this);
mNavigationBarA11yHelper.removeA11yEventListener(mAccessibilityListener);
@@ -686,16 +673,22 @@
// before notifications creation. We cannot directly use getLightBarController()
// from NavigationBarFragment directly.
LightBarController lightBarController = mIsOnDefaultDisplay
- ? mMainLightBarController : mLightBarControllerFactory.create(mContext);
+ ? Dependency.get(LightBarController.class)
+ : new LightBarController(mContext,
+ Dependency.get(DarkIconDispatcher.class),
+ Dependency.get(BatteryController.class),
+ Dependency.get(NavigationModeController.class),
+ Dependency.get(DumpManager.class));
setLightBarController(lightBarController);
// TODO(b/118592525): to support multi-display, we start to add something which is
// per-display, while others may be global. I think it's time to
// add a new class maybe named DisplayDependency to solve
// per-display Dependency problem.
- // Alternative: this is a good case for a Dagger subcomponent. Same with LightBarController.
AutoHideController autoHideController = mIsOnDefaultDisplay
- ? mMainAutoHideController : mAutoHideControllerFactory.create(mContext);
+ ? Dependency.get(AutoHideController.class)
+ : new AutoHideController(mContext, mHandler,
+ Dependency.get(IWindowManager.class));
setAutoHideController(autoHideController);
restoreAppearanceAndTransientState();
}
@@ -1190,8 +1183,9 @@
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mHomeBlockedThisTouch = false;
- if (mTelecomManagerOptional.isPresent()
- && mTelecomManagerOptional.get().isRinging()) {
+ TelecomManager telecomManager =
+ mContext.getSystemService(TelecomManager.class);
+ if (telecomManager != null && telecomManager.isRinging()) {
if (statusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
"No heads up");
@@ -1273,7 +1267,7 @@
}
private void onImeSwitcherClick(View v) {
- mInputMethodManager.showInputMethodPickerFromSystem(
+ mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem(
true /* showAuxiliarySubtypes */, mDisplayId);
};
@@ -1708,123 +1702,4 @@
int getNavigationIconHints() {
return mNavigationIconHints;
}
-
- /**
- * Injectable factory for construction a {@link NavigationBar}.
- */
- public static class Factory {
- private final WindowManager mWindowManager;
- private final Lazy<AssistManager> mAssistManagerLazy;
- private final AccessibilityManager mAccessibilityManager;
- private final DeviceProvisionedController mDeviceProvisionedController;
- private final MetricsLogger mMetricsLogger;
- private final OverviewProxyService mOverviewProxyService;
- private final NavigationModeController mNavigationModeController;
- private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
- private final StatusBarStateController mStatusBarStateController;
- private final SysUiState mSysUiFlagsContainer;
- private final BroadcastDispatcher mBroadcastDispatcher;
- private final CommandQueue mCommandQueue;
- private final Optional<Pip> mPipOptional;
- private final Optional<LegacySplitScreen> mSplitScreenOptional;
- private final Optional<Recents> mRecentsOptional;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
- private final ShadeController mShadeController;
- private final NotificationRemoteInputManager mNotificationRemoteInputManager;
- private final NotificationShadeDepthController mNotificationShadeDepthController;
- private final SystemActions mSystemActions;
- private final Handler mMainHandler;
- private final NavigationBarOverlayController mNavbarOverlayController;
- private final UiEventLogger mUiEventLogger;
- private final NavigationBarA11yHelper mNavigationBarA11yHelper;
- private final UserTracker mUserTracker;
- private final LightBarController mMainLightBarController;
- private final LightBarController.Factory mLightBarControllerFactory;
- private final AutoHideController mMainAutoHideController;
- private final AutoHideController.Factory mAutoHideControllerFactory;
- private final Optional<TelecomManager> mTelecomManagerOptional;
- private final InputMethodManager mInputMethodManager;
-
- @Inject
- public Factory(
- WindowManager windowManager,
- Lazy<AssistManager> assistManagerLazy,
- AccessibilityManager accessibilityManager,
- DeviceProvisionedController deviceProvisionedController,
- MetricsLogger metricsLogger,
- OverviewProxyService overviewProxyService,
- NavigationModeController navigationModeController,
- AccessibilityButtonModeObserver accessibilityButtonModeObserver,
- StatusBarStateController statusBarStateController,
- SysUiState sysUiFlagsContainer,
- BroadcastDispatcher broadcastDispatcher,
- CommandQueue commandQueue,
- Optional<Pip> pipOptional,
- Optional<LegacySplitScreen> splitScreenOptional,
- Optional<Recents> recentsOptional,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
- ShadeController shadeController,
- NotificationRemoteInputManager notificationRemoteInputManager,
- NotificationShadeDepthController notificationShadeDepthController,
- SystemActions systemActions,
- @Main Handler mainHandler,
- NavigationBarOverlayController navbarOverlayController,
- UiEventLogger uiEventLogger,
- NavigationBarA11yHelper navigationBarA11yHelper,
- UserTracker userTracker,
- LightBarController mainLightBarController,
- LightBarController.Factory lightBarControllerFactory,
- AutoHideController mainAutoHideController,
- AutoHideController.Factory autoHideControllerFactory,
- Optional<TelecomManager> telecomManagerOptional,
- InputMethodManager inputMethodManager) {
- mWindowManager = windowManager;
- mAssistManagerLazy = assistManagerLazy;
- mAccessibilityManager = accessibilityManager;
- mDeviceProvisionedController = deviceProvisionedController;
- mMetricsLogger = metricsLogger;
- mOverviewProxyService = overviewProxyService;
- mNavigationModeController = navigationModeController;
- mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
- mStatusBarStateController = statusBarStateController;
- mSysUiFlagsContainer = sysUiFlagsContainer;
- mBroadcastDispatcher = broadcastDispatcher;
- mCommandQueue = commandQueue;
- mPipOptional = pipOptional;
- mSplitScreenOptional = splitScreenOptional;
- mRecentsOptional = recentsOptional;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
- mShadeController = shadeController;
- mNotificationRemoteInputManager = notificationRemoteInputManager;
- mNotificationShadeDepthController = notificationShadeDepthController;
- mSystemActions = systemActions;
- mMainHandler = mainHandler;
- mNavbarOverlayController = navbarOverlayController;
- mUiEventLogger = uiEventLogger;
- mNavigationBarA11yHelper = navigationBarA11yHelper;
- mUserTracker = userTracker;
- mMainLightBarController = mainLightBarController;
- mLightBarControllerFactory = lightBarControllerFactory;
- mMainAutoHideController = mainAutoHideController;
- mAutoHideControllerFactory = autoHideControllerFactory;
- mTelecomManagerOptional = telecomManagerOptional;
- mInputMethodManager = inputMethodManager;
- }
-
- /** Construct a {@link NavigationBar} */
- public NavigationBar create(Context context) {
- return new NavigationBar(context, mWindowManager, mAssistManagerLazy,
- mAccessibilityManager, mDeviceProvisionedController, mMetricsLogger,
- mOverviewProxyService, mNavigationModeController,
- mAccessibilityButtonModeObserver, mStatusBarStateController,
- mSysUiFlagsContainer, mBroadcastDispatcher, mCommandQueue, mPipOptional,
- mSplitScreenOptional, mRecentsOptional, mStatusBarOptionalLazy,
- mShadeController, mNotificationRemoteInputManager,
- mNotificationShadeDepthController, mSystemActions, mMainHandler,
- mNavbarOverlayController, mUiEventLogger, mNavigationBarA11yHelper,
- mUserTracker, mMainLightBarController, mLightBarControllerFactory,
- mMainAutoHideController, mAutoHideControllerFactory, mTelecomManagerOptional,
- mInputMethodManager);
- }
- }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 8dc6b99..a1a630a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -32,30 +32,52 @@
import android.view.Display;
import android.view.IWindowManager;
import android.view.View;
+import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.RegisterStatusBarResult;
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.systemui.Dumpable;
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.accessibility.SystemActions;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.pip.Pip;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Optional;
import javax.inject.Inject;
+import dagger.Lazy;
+
/** A controller to handle navigation bars. */
@SysUISingleton
@@ -68,12 +90,36 @@
private static final String TAG = NavigationBarController.class.getSimpleName();
private final Context mContext;
+ private final WindowManager mWindowManager;
+ private final Lazy<AssistManager> mAssistManagerLazy;
+ private final AccessibilityManager mAccessibilityManager;
+ private final AccessibilityManagerWrapper mAccessibilityManagerWrapper;
+ private final DeviceProvisionedController mDeviceProvisionedController;
+ private final MetricsLogger mMetricsLogger;
+ private final OverviewProxyService mOverviewProxyService;
+ private final NavigationModeController mNavigationModeController;
+ private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ private final StatusBarStateController mStatusBarStateController;
+ private final SysUiState mSysUiFlagsContainer;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final CommandQueue mCommandQueue;
+ private final Optional<Pip> mPipOptional;
+ private final Optional<LegacySplitScreen> mSplitScreenOptional;
+ private final Optional<Recents> mRecentsOptional;
+ private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final ShadeController mShadeController;
+ private final NotificationRemoteInputManager mNotificationRemoteInputManager;
+ private final SystemActions mSystemActions;
+ private final UiEventLogger mUiEventLogger;
private final Handler mHandler;
- private final NavigationBar.Factory mNavigationBarFactory;
+ private final NavigationBarA11yHelper mNavigationBarA11yHelper;
private final DisplayManager mDisplayManager;
+ private final NavigationBarOverlayController mNavBarOverlayController;
private final TaskbarDelegate mTaskbarDelegate;
+ private final NotificationShadeDepthController mNotificationShadeDepthController;
private int mNavMode;
@VisibleForTesting boolean mIsTablet;
+ private final UserTracker mUserTracker;
/** A displayId - nav bar maps. */
@VisibleForTesting
@@ -86,28 +132,72 @@
@Inject
public NavigationBarController(Context context,
+ WindowManager windowManager,
+ Lazy<AssistManager> assistManagerLazy,
+ AccessibilityManager accessibilityManager,
+ AccessibilityManagerWrapper accessibilityManagerWrapper,
+ DeviceProvisionedController deviceProvisionedController,
+ MetricsLogger metricsLogger,
OverviewProxyService overviewProxyService,
NavigationModeController navigationModeController,
+ AccessibilityButtonModeObserver accessibilityButtonModeObserver,
+ StatusBarStateController statusBarStateController,
SysUiState sysUiFlagsContainer,
+ BroadcastDispatcher broadcastDispatcher,
CommandQueue commandQueue,
+ Optional<Pip> pipOptional,
+ Optional<LegacySplitScreen> splitScreenOptional,
+ Optional<Recents> recentsOptional,
+ Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ ShadeController shadeController,
+ NotificationRemoteInputManager notificationRemoteInputManager,
+ NotificationShadeDepthController notificationShadeDepthController,
+ SystemActions systemActions,
@Main Handler mainHandler,
+ UiEventLogger uiEventLogger,
+ NavigationBarOverlayController navBarOverlayController,
ConfigurationController configurationController,
NavigationBarA11yHelper navigationBarA11yHelper,
TaskbarDelegate taskbarDelegate,
- NavigationBar.Factory navigationBarFactory,
+ UserTracker userTracker,
DumpManager dumpManager) {
mContext = context;
+ mWindowManager = windowManager;
+ mAssistManagerLazy = assistManagerLazy;
+ mAccessibilityManager = accessibilityManager;
+ mAccessibilityManagerWrapper = accessibilityManagerWrapper;
+ mDeviceProvisionedController = deviceProvisionedController;
+ mMetricsLogger = metricsLogger;
+ mOverviewProxyService = overviewProxyService;
+ mNavigationModeController = navigationModeController;
+ mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
+ mStatusBarStateController = statusBarStateController;
+ mSysUiFlagsContainer = sysUiFlagsContainer;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mCommandQueue = commandQueue;
+ mPipOptional = pipOptional;
+ mSplitScreenOptional = splitScreenOptional;
+ mRecentsOptional = recentsOptional;
+ mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mShadeController = shadeController;
+ mNotificationRemoteInputManager = notificationRemoteInputManager;
+ mNotificationShadeDepthController = notificationShadeDepthController;
+ mSystemActions = systemActions;
+ mUiEventLogger = uiEventLogger;
mHandler = mainHandler;
- mNavigationBarFactory = navigationBarFactory;
+ mNavigationBarA11yHelper = navigationBarA11yHelper;
mDisplayManager = mContext.getSystemService(DisplayManager.class);
commandQueue.addCallback(this);
configurationController.addCallback(this);
mConfigChanges.applyNewConfig(mContext.getResources());
- mNavMode = navigationModeController.addListener(this);
+ mNavBarOverlayController = navBarOverlayController;
+ mNavMode = mNavigationModeController.addListener(this);
+ mNavigationModeController.addListener(this);
mTaskbarDelegate = taskbarDelegate;
mTaskbarDelegate.setOverviewProxyService(commandQueue, overviewProxyService,
navigationBarA11yHelper, navigationModeController, sysUiFlagsContainer);
mIsTablet = isTablet(mContext);
+ mUserTracker = userTracker;
dumpManager.registerDumpable(this);
}
@@ -264,8 +354,33 @@
final Context context = isOnDefaultDisplay
? mContext
: mContext.createDisplayContext(display);
- NavigationBar navBar = mNavigationBarFactory.create(context);
-
+ NavigationBar navBar = new NavigationBar(context,
+ mWindowManager,
+ mAssistManagerLazy,
+ mAccessibilityManager,
+ mAccessibilityManagerWrapper,
+ mDeviceProvisionedController,
+ mMetricsLogger,
+ mOverviewProxyService,
+ mNavigationModeController,
+ mAccessibilityButtonModeObserver,
+ mStatusBarStateController,
+ mSysUiFlagsContainer,
+ mBroadcastDispatcher,
+ mCommandQueue,
+ mPipOptional,
+ mSplitScreenOptional,
+ mRecentsOptional,
+ mStatusBarOptionalLazy,
+ mShadeController,
+ mNotificationRemoteInputManager,
+ mNotificationShadeDepthController,
+ mSystemActions,
+ mHandler,
+ mNavBarOverlayController,
+ mUiEventLogger,
+ mNavigationBarA11yHelper,
+ mUserTracker);
mNavigationBars.put(displayId, navBar);
View navigationBarView = navBar.createView(savedState);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 71d2a73..fa616921 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -20,6 +20,7 @@
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
@@ -34,9 +35,13 @@
import android.app.StatusBarManager;
import android.app.StatusBarManager.WindowVisibleState;
+import android.content.ComponentCallbacks;
import android.content.Context;
+import android.content.res.Configuration;
+import android.hardware.display.DisplayManager;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
+import android.view.Display;
import android.view.InsetsVisibilities;
import android.view.View;
import android.view.WindowInsetsController.Behavior;
@@ -55,7 +60,8 @@
@Singleton
public class TaskbarDelegate implements CommandQueue.Callbacks,
- OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener {
+ OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
+ ComponentCallbacks {
private final EdgeBackGestureHandler mEdgeBackGestureHandler;
@@ -71,11 +77,16 @@
private int mDisabledFlags;
private @WindowVisibleState int mTaskBarWindowState = WINDOW_STATE_SHOWING;
private @Behavior int mBehavior;
+ private final Context mContext;
+ private final DisplayManager mDisplayManager;
+ private Context mWindowContext;
@Inject
public TaskbarDelegate(Context context) {
mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.Factory.class)
.create(context);
+ mContext = context;
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
}
public void setOverviewProxyService(CommandQueue commandQueue,
@@ -97,6 +108,10 @@
mNavigationModeController.removeListener(this);
mNavigationBarA11yHelper.removeA11yEventListener(mNavA11yEventListener);
mEdgeBackGestureHandler.onNavBarDetached();
+ if (mWindowContext != null) {
+ mWindowContext.unregisterComponentCallbacks(this);
+ mWindowContext = null;
+ }
}
public void init(int displayId) {
@@ -107,6 +122,10 @@
mNavigationModeController.addListener(this));
mNavigationBarA11yHelper.registerA11yEventListener(mNavA11yEventListener);
mEdgeBackGestureHandler.onNavBarAttached();
+ // Initialize component callback
+ Display display = mDisplayManager.getDisplay(displayId);
+ mWindowContext = mContext.createWindowContext(display, TYPE_APPLICATION, null);
+ mWindowContext.registerComponentCallbacks(this);
// Set initial state for any listeners
updateSysuiFlags();
}
@@ -193,4 +212,12 @@
private boolean allowSystemGestureIgnoringBarVisibility() {
return mBehavior != BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
}
+
+ @Override
+ public void onConfigurationChanged(Configuration configuration) {
+ mEdgeBackGestureHandler.onConfigurationChanged(configuration);
+ }
+
+ @Override
+ public void onLowMemory() {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index 953f9fb..fec61d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -42,8 +42,8 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
import com.android.systemui.util.CarrierConfigTracker;
import java.util.function.Consumer;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 04f089d..9de6ceb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -51,13 +51,13 @@
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
import java.util.ArrayList;
import java.util.LinkedHashMap;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index b1af841..35dadd4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -56,11 +56,11 @@
import com.android.systemui.qs.SignalTileView;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import javax.inject.Inject;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 530804e..98d0a72 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -52,13 +52,13 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
-import com.android.systemui.statusbar.policy.WifiIcons;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.WifiIcons;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 41a3020..e6e7e21 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -52,11 +52,11 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
-import com.android.systemui.statusbar.policy.WifiIcons;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.WifiIcons;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index dc54e1b..11430d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -206,7 +206,7 @@
window.setContentView(mDialogView);
//Only fix the width for large screen or tablet.
window.setLayout(mContext.getResources().getDimensionPixelSize(
- R.dimen.internet_dialog_list_max_width), ViewGroup.LayoutParams.WRAP_CONTENT);
+ R.dimen.large_dialog_width), ViewGroup.LayoutParams.WRAP_CONTENT);
window.setWindowAnimations(R.style.Animation_InternetDialog);
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
window.addFlags(FLAG_LAYOUT_NO_LIMITS);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index aaba5ef..b9cd08e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -75,9 +75,9 @@
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.toast.SystemUIToast;
import com.android.systemui.toast.ToastFactory;
import com.android.systemui.util.CarrierConfigTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 31d51f1..a42b34c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -326,18 +326,11 @@
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
mTransitionView.setImageBitmap(mOutputBitmap);
+ mTransitionView.setVisibility(View.VISIBLE);
mTransitionView.setTransitionName(
ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME);
// TODO: listen for transition completing instead of finishing onStop
mTransitionStarted = true;
- int[] locationOnScreen = new int[2];
- mTransitionView.getLocationOnScreen(locationOnScreen);
- int[] locationInWindow = new int[2];
- mTransitionView.getLocationInWindow(locationInWindow);
- int deltaX = locationOnScreen[0] - locationInWindow[0];
- int deltaY = locationOnScreen[1] - locationInWindow[1];
- mTransitionView.setX(mTransitionView.getX() - deltaX);
- mTransitionView.setY(mTransitionView.getY() - deltaY);
startActivity(intent,
ActivityOptions.makeSceneTransitionAnimation(this, mTransitionView,
ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME).toBundle());
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index c8a97e9..7d26857 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -940,12 +940,10 @@
*/
private Supplier<ActionTransition> getActionTransitionSupplier() {
return () -> {
- View preview = mScreenshotView.getTransitionView();
- preview.setX(preview.getX() - mScreenshotView.getStaticLeftMargin());
Pair<ActivityOptions, ExitTransitionCoordinator> transition =
ActivityOptions.startSharedElementAnimation(
mWindow, new ScreenshotExitTransitionCallbacksSupplier(true).get(),
- null, Pair.create(mScreenshotView.getTransitionView(),
+ null, Pair.create(mScreenshotView.getScreenshotPreview(),
ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME));
transition.second.startExit();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 01dc0ca..e812397 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -137,13 +137,11 @@
private int mNavMode;
private boolean mOrientationPortrait;
private boolean mDirectionLTR;
- private int mStaticLeftMargin;
private ScreenshotSelectorView mScreenshotSelectorView;
private ImageView mScrollingScrim;
private View mScreenshotStatic;
private ImageView mScreenshotPreview;
- private View mTransitionView;
private View mScreenshotPreviewBorder;
private ImageView mScrollablePreview;
private ImageView mScreenshotFlash;
@@ -341,7 +339,7 @@
mScrollingScrim = requireNonNull(findViewById(R.id.screenshot_scrolling_scrim));
mScreenshotStatic = requireNonNull(findViewById(R.id.screenshot_static));
mScreenshotPreview = requireNonNull(findViewById(R.id.screenshot_preview));
- mTransitionView = requireNonNull(findViewById(R.id.screenshot_transition_view));
+
mScreenshotPreviewBorder = requireNonNull(
findViewById(R.id.screenshot_preview_border));
mScreenshotPreview.setClipToOutline(true);
@@ -387,12 +385,8 @@
requestFocus();
}
- View getTransitionView() {
- return mTransitionView;
- }
-
- int getStaticLeftMargin() {
- return mStaticLeftMargin;
+ View getScreenshotPreview() {
+ return mScreenshotPreview;
}
/**
@@ -442,7 +436,6 @@
Math.max(navBarInsets.bottom, waterfall.bottom));
}
}
- mStaticLeftMargin = p.leftMargin;
mScreenshotStatic.setLayoutParams(p);
mScreenshotStatic.requestLayout();
}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
new file mode 100644
index 0000000..c50365f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
@@ -0,0 +1,74 @@
+package com.android.systemui.sensorprivacy
+
+import android.content.Context
+import android.content.DialogInterface
+import android.content.res.Resources
+import android.text.Html
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager
+import android.widget.ImageView
+import com.android.internal.widget.DialogTitle
+import com.android.systemui.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+
+class SensorUseDialog(
+ context: Context,
+ val sensor: Int,
+ val clickListener: DialogInterface.OnClickListener
+) : SystemUIDialog(context) {
+
+ // TODO move to onCreate (b/200815309)
+ init {
+ window!!.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
+ window!!.addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS)
+
+ val layoutInflater = LayoutInflater.from(context)
+ val customTitleView = layoutInflater.inflate(R.layout.sensor_use_started_title, null)
+ customTitleView.requireViewById<DialogTitle>(R.id.sensor_use_started_title_message)
+ .setText(when (sensor) {
+ SensorUseStartedActivity.MICROPHONE ->
+ R.string.sensor_privacy_start_use_mic_dialog_title
+ SensorUseStartedActivity.CAMERA ->
+ R.string.sensor_privacy_start_use_camera_dialog_title
+ SensorUseStartedActivity.ALL_SENSORS ->
+ R.string.sensor_privacy_start_use_mic_camera_dialog_title
+ else -> Resources.ID_NULL
+ })
+ customTitleView.requireViewById<ImageView>(R.id.sensor_use_microphone_icon).visibility =
+ if (sensor == SensorUseStartedActivity.MICROPHONE ||
+ sensor == SensorUseStartedActivity.ALL_SENSORS) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
+ customTitleView.requireViewById<ImageView>(R.id.sensor_use_camera_icon).visibility =
+ if (sensor == SensorUseStartedActivity.CAMERA ||
+ sensor == SensorUseStartedActivity.ALL_SENSORS) {
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
+
+ setCustomTitle(customTitleView)
+ setMessage(Html.fromHtml(context.getString(when (sensor) {
+ SensorUseStartedActivity.MICROPHONE ->
+ R.string.sensor_privacy_start_use_mic_dialog_content
+ SensorUseStartedActivity.CAMERA ->
+ R.string.sensor_privacy_start_use_camera_dialog_content
+ SensorUseStartedActivity.ALL_SENSORS ->
+ R.string.sensor_privacy_start_use_mic_camera_dialog_content
+ else -> Resources.ID_NULL
+ }), 0))
+
+ setButton(BUTTON_POSITIVE,
+ context.getString(com.android.internal.R.string
+ .sensor_privacy_start_use_dialog_turn_on_button), clickListener)
+ setButton(BUTTON_NEGATIVE,
+ context.getString(com.android.internal.R.string
+ .cancel), clickListener)
+
+ setCancelable(false)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index f0fb5eb..b0071d9 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -16,33 +16,28 @@
package com.android.systemui.sensorprivacy
+import android.app.Activity
+import android.app.AlertDialog
import android.content.DialogInterface
+import android.content.DialogInterface.BUTTON_NEGATIVE
+import android.content.DialogInterface.BUTTON_POSITIVE
import android.content.Intent
import android.content.Intent.EXTRA_PACKAGE_NAME
-import android.content.pm.PackageManager
-import android.content.res.Resources
import android.hardware.SensorPrivacyManager
import android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS
import android.hardware.SensorPrivacyManager.EXTRA_SENSOR
import android.hardware.SensorPrivacyManager.Sources.DIALOG
import android.os.Bundle
import android.os.Handler
-import android.text.Html
-import android.view.View.GONE
-import android.view.View.VISIBLE
-import android.widget.ImageView
-import com.android.internal.app.AlertActivity
-import com.android.internal.widget.DialogTitle
-import com.android.systemui.R
+import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION
+import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL
+import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE
+import com.android.internal.util.FrameworkStatsLog.write
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
-import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION
-import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE
-import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL
-import com.android.internal.util.FrameworkStatsLog.write
/**
* Dialog to be shown on top of apps that are attempting to use a sensor (e.g. microphone) which is
@@ -55,7 +50,7 @@
private val keyguardStateController: KeyguardStateController,
private val keyguardDismissUtil: KeyguardDismissUtil,
@Background private val bgHandler: Handler
-) : AlertActivity(), DialogInterface.OnClickListener {
+) : Activity(), DialogInterface.OnClickListener {
companion object {
private val LOG_TAG = SensorUseStartedActivity::class.java.simpleName
@@ -63,9 +58,9 @@
private const val SUPPRESS_REMINDERS_REMOVAL_DELAY_MILLIS = 2000L
private const val UNLOCK_DELAY_MILLIS = 200L
- private const val CAMERA = SensorPrivacyManager.Sensors.CAMERA
- private const val MICROPHONE = SensorPrivacyManager.Sensors.MICROPHONE
- private const val ALL_SENSORS = Integer.MAX_VALUE
+ internal const val CAMERA = SensorPrivacyManager.Sensors.CAMERA
+ internal const val MICROPHONE = SensorPrivacyManager.Sensors.MICROPHONE
+ internal const val ALL_SENSORS = Integer.MAX_VALUE
}
private var sensor = -1
@@ -74,6 +69,8 @@
private lateinit var sensorPrivacyListener: IndividualSensorPrivacyController.Callback
+ private var mDialog: AlertDialog? = null
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -91,7 +88,7 @@
IndividualSensorPrivacyController.Callback { _, _ ->
if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
!sensorPrivacyController.isSensorBlocked(CAMERA)) {
- dismiss()
+ finish()
}
}
@@ -109,71 +106,22 @@
}
}
sensorPrivacyListener =
- IndividualSensorPrivacyController.Callback {
- whichSensor: Int, isBlocked: Boolean ->
+ IndividualSensorPrivacyController.Callback { whichSensor: Int,
+ isBlocked: Boolean ->
if (whichSensor == sensor && !isBlocked) {
- dismiss()
+ finish()
}
}
sensorPrivacyController.addCallback(sensorPrivacyListener)
- sensorPrivacyController.addCallback { _, isBlocked ->
- if (!isBlocked) {
- dismiss()
- }
- }
- }
-
- mAlertParams.apply {
- try {
- mCustomTitleView = mInflater.inflate(R.layout.sensor_use_started_title, null)
- mCustomTitleView.findViewById<DialogTitle>(R.id.sensor_use_started_title_message)!!
- .setText(when (sensor) {
- MICROPHONE ->
- R.string.sensor_privacy_start_use_mic_dialog_title
- CAMERA ->
- R.string.sensor_privacy_start_use_camera_dialog_title
- ALL_SENSORS ->
- R.string.sensor_privacy_start_use_mic_camera_dialog_title
- else -> Resources.ID_NULL
- })
-
- mCustomTitleView.findViewById<ImageView>(R.id.sensor_use_microphone_icon)!!
- .visibility = if (sensor == MICROPHONE || sensor == ALL_SENSORS) {
- VISIBLE
- } else {
- GONE
- }
- mCustomTitleView.findViewById<ImageView>(R.id.sensor_use_camera_icon)!!
- .visibility = if (sensor == CAMERA || sensor == ALL_SENSORS) {
- VISIBLE
- } else {
- GONE
- }
-
- mMessage = Html.fromHtml(getString(when (sensor) {
- MICROPHONE ->
- R.string.sensor_privacy_start_use_mic_dialog_content
- CAMERA ->
- R.string.sensor_privacy_start_use_camera_dialog_content
- ALL_SENSORS ->
- R.string.sensor_privacy_start_use_mic_camera_dialog_content
- else -> Resources.ID_NULL
- }, packageManager.getApplicationInfo(sensorUsePackageName, 0)
- .loadLabel(packageManager)), 0)
- } catch (e: PackageManager.NameNotFoundException) {
+ if (!sensorPrivacyController.isSensorBlocked(sensor)) {
finish()
return
}
-
- mPositiveButtonText = getString(
- com.android.internal.R.string.sensor_privacy_start_use_dialog_turn_on_button)
- mNegativeButtonText = getString(android.R.string.cancel)
- mPositiveButtonListener = this@SensorUseStartedActivity
- mNegativeButtonListener = this@SensorUseStartedActivity
}
- setupAlert()
+ mDialog = SensorUseDialog(this, sensor, this)
+ mDialog!!.show()
}
override fun onStart() {
@@ -212,7 +160,7 @@
}
}
- dismiss()
+ finish()
}
override fun onStop() {
@@ -229,6 +177,7 @@
override fun onDestroy() {
super.onDestroy()
+ mDialog?.dismiss()
sensorPrivacyController.removeCallback(sensorPrivacyListener)
}
@@ -263,4 +212,4 @@
.suppressSensorPrivacyReminders(sensor, suppressed)
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
index 8cd3632..cc5cf4b 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
@@ -24,6 +24,7 @@
import android.os.Bundle;
import android.util.Log;
import android.view.View;
+import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
@@ -57,6 +58,8 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ getWindow().addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
boolean allSensors = getIntent().getBooleanExtra(SensorPrivacyManager.EXTRA_ALL_SENSORS,
false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
index e49f48f..bcba5cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameViewController.java
@@ -26,7 +26,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.ViewController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
index f54bb68..828a129 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import android.content.Context;
import android.content.Intent;
@@ -54,6 +54,7 @@
import javax.inject.Inject;
+/** */
public class AccessPointControllerImpl
implements NetworkController.AccessPointController,
WifiPickerTracker.WifiPickerTrackerCallback, LifecycleOwner {
@@ -115,17 +116,19 @@
super.finalize();
}
+ /** */
public boolean canConfigWifi() {
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI,
new UserHandle(mCurrentUser));
}
+ /** */
public boolean canConfigMobileData() {
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
UserHandle.of(mCurrentUser)) && mUserTracker.getUserInfo().isAdmin();
}
- public void onUserSwitched(int newUserId) {
+ void onUserSwitched(int newUserId) {
mCurrentUser = newUserId;
}
@@ -224,7 +227,7 @@
}
}
- public void dump(PrintWriter pw) {
+ void dump(PrintWriter pw) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
ipw.println("AccessPointControllerImpl:");
ipw.increaseIndent();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
index 7ac6d63..052a789 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import android.os.Handler;
import android.os.Looper;
@@ -22,11 +22,11 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
@@ -235,7 +235,7 @@
.append("icon=").append(icon)
.toString();
recordLastCallback(log);
- obtainMessage(MSG_ETHERNET_CHANGED, icon).sendToTarget();;
+ obtainMessage(MSG_ETHERNET_CHANGED, icon).sendToTarget();
}
@Override
@@ -252,14 +252,14 @@
.toString();
recordLastCallback(log);
}
- obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget();;
+ obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget();
}
- public void setListening(EmergencyListener listener, boolean listening) {
+ void setListening(EmergencyListener listener, boolean listening) {
obtainMessage(MSG_ADD_REMOVE_EMERGENCY, listening ? 1 : 0, 0, listener).sendToTarget();
}
- public void setListening(SignalCallback listener, boolean listening) {
+ void setListening(SignalCallback listener, boolean listening) {
obtainMessage(MSG_ADD_REMOVE_SIGNAL, listening ? 1 : 0, 0, listener).sendToTarget();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetIcons.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetIcons.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetIcons.java
index b391bd9..196aad9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetIcons.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java
index 80b75a7..c9d40ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import android.content.Context;
import android.net.NetworkCapabilities;
@@ -21,12 +21,12 @@
import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.SignalIcon.IconGroup;
import com.android.settingslib.SignalIcon.State;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import java.util.BitSet;
-
+/** */
public class EthernetSignalController extends
SignalController<State, IconGroup> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index a543c7c..20ef4ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import static com.android.settingslib.mobile.MobileMappings.getDefaultIcons;
import static com.android.settingslib.mobile.MobileMappings.getIconKey;
@@ -58,9 +58,9 @@
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import com.android.systemui.util.CarrierConfigTracker;
import java.io.PrintWriter;
@@ -245,7 +245,7 @@
mProviderModelSetting = featureFlags.isProviderModelSettingEnabled();
}
- public void setConfiguration(Config config) {
+ void setConfiguration(Config config) {
mConfig = config;
updateInflateSignalStrength();
mNetworkToIconLookup = mapIconSets(mConfig);
@@ -253,12 +253,12 @@
updateTelephony();
}
- public void setAirplaneMode(boolean airplaneMode) {
+ void setAirplaneMode(boolean airplaneMode) {
mCurrentState.airplaneMode = airplaneMode;
notifyListenersIfNecessary();
}
- public void setUserSetupComplete(boolean userSetup) {
+ void setUserSetupComplete(boolean userSetup) {
mCurrentState.userSetup = userSetup;
notifyListenersIfNecessary();
}
@@ -272,7 +272,7 @@
notifyListenersIfNecessary();
}
- public void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) {
+ void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) {
mCurrentState.carrierNetworkChangeMode = carrierNetworkChangeMode;
updateTelephony();
}
@@ -413,7 +413,7 @@
if (mCurrentState.dataSim) {
// If using provider model behavior, only show QS icons if the state is also default
if (pm && !mCurrentState.isDefault) {
- return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
+ return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
}
if (mCurrentState.showQuickSettingsRatIcon() || mConfig.alwaysShowDataRatIcon) {
@@ -501,7 +501,7 @@
return mCurrentState.carrierNetworkChangeMode;
}
- public void handleBroadcast(Intent intent) {
+ void handleBroadcast(Intent intent) {
String action = intent.getAction();
if (action.equals(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)) {
updateNetworkName(intent.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false),
@@ -740,10 +740,10 @@
* mTelephonyDisplayInfo, and mSimState. It should be called any time one of these is updated.
* This will call listeners if necessary.
*/
- private final void updateTelephony() {
+ private void updateTelephony() {
if (Log.isLoggable(mTag, Log.DEBUG)) {
- Log.d(mTag, "updateTelephonySignalStrength: hasService=" +
- Utils.isInService(mServiceState) + " ss=" + mSignalStrength
+ Log.d(mTag, "updateTelephonySignalStrength: hasService="
+ + Utils.isInService(mServiceState) + " ss=" + mSignalStrength
+ " displayInfo=" + mTelephonyDisplayInfo);
}
checkDefaultData();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkController.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkController.java
index 9aec903..1433096 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkController.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import android.content.Context;
import android.content.Intent;
@@ -22,29 +22,44 @@
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.demomode.DemoMode;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.wifitrackerlib.MergedCarrierEntry;
import com.android.wifitrackerlib.WifiEntry;
import java.util.List;
+/** */
public interface NetworkController extends CallbackController<SignalCallback>, DemoMode {
-
+ /** */
boolean hasMobileDataFeature();
+ /** */
void setWifiEnabled(boolean enabled);
+ /** */
AccessPointController getAccessPointController();
+ /** */
DataUsageController getMobileDataController();
+ /** */
DataSaverController getDataSaverController();
+ /** */
String getMobileDataNetworkName();
+ /** */
boolean isMobileDataNetworkInService();
+ /** */
int getNumberSubscriptions();
+ /** */
boolean hasVoiceCallingFeature();
+ /** */
void addEmergencyListener(EmergencyListener listener);
+ /** */
void removeEmergencyListener(EmergencyListener listener);
+ /** */
boolean hasEmergencyCryptKeeperText();
+ /** */
boolean isRadioOn();
/**
@@ -143,7 +158,8 @@
}
}
- public interface SignalCallback {
+ /** */
+ interface SignalCallback {
/**
* Callback for listeners to be able to update the state of any UI tracking connectivity of
* WiFi networks.
@@ -156,14 +172,19 @@
*/
default void setMobileDataIndicators(MobileDataIndicators mobileDataIndicators) {}
+ /** */
default void setSubs(List<SubscriptionInfo> subs) {}
+ /** */
default void setNoSims(boolean show, boolean simDetected) {}
+ /** */
default void setEthernetIndicators(IconState icon) {}
+ /** */
default void setIsAirplaneMode(IconState icon) {}
+ /** */
default void setMobileDataEnabled(boolean enabled) {}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 3bfc2a6..0bd841f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -76,7 +76,12 @@
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.qs.tiles.dialog.InternetDialogUtil;
import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.DataSaverControllerImpl;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
+import com.android.systemui.statusbar.policy.EncryptionHelper;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.CarrierConfigTracker;
@@ -376,11 +381,10 @@
@Override
public void onCapabilitiesChanged(
- Network network, NetworkCapabilities networkCapabilities) {
- boolean lastValidated = (mLastNetworkCapabilities != null) &&
- mLastNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);
- boolean validated =
- networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);
+ Network network, NetworkCapabilities networkCapabilities) {
+ boolean lastValidated = (mLastNetworkCapabilities != null)
+ && mLastNetworkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);
+ boolean validated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED);
// This callback is invoked a lot (i.e. when RSSI changes), so avoid updating
// icons when connectivity state has remained the same.
@@ -544,19 +548,23 @@
return mDataUsageController;
}
+ /** */
public void addEmergencyListener(EmergencyListener listener) {
mCallbackHandler.setListening(listener, true);
mCallbackHandler.setEmergencyCallsOnly(isEmergencyOnly());
}
+ /** */
public void removeEmergencyListener(EmergencyListener listener) {
mCallbackHandler.setListening(listener, false);
}
+ /** */
public boolean hasMobileDataFeature() {
return mHasMobileDataFeature;
}
+ /** */
public boolean hasVoiceCallingFeature() {
return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
}
@@ -661,7 +669,7 @@
}
}
- public boolean isEmergencyOnly() {
+ boolean isEmergencyOnly() {
if (mMobileSignalControllers.size() == 0) {
// When there are no active subscriptions, determine emengency state from last
// broadcast.
@@ -690,8 +698,10 @@
if (mMobileSignalControllers.size() == 1) {
mEmergencySource = EMERGENCY_ASSUMED_VOICE_CONTROLLER
+ mMobileSignalControllers.keyAt(0);
- if (DEBUG) Log.d(TAG, "Getting assumed emergency from "
- + mMobileSignalControllers.keyAt(0));
+ if (DEBUG) {
+ Log.d(TAG, "Getting assumed emergency from "
+ + mMobileSignalControllers.keyAt(0));
+ }
return mMobileSignalControllers.valueAt(0).getState().isEmergency;
}
if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId);
@@ -915,7 +925,7 @@
@GuardedBy("mLock")
@VisibleForTesting
- public void setCurrentSubscriptionsLocked(List<SubscriptionInfo> subscriptions) {
+ void setCurrentSubscriptionsLocked(List<SubscriptionInfo> subscriptions) {
Collections.sort(subscriptions, new Comparator<SubscriptionInfo>() {
@Override
public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) {
@@ -1125,6 +1135,7 @@
mEthernetSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports);
}
+ /** */
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("NetworkController state:");
@@ -1178,7 +1189,7 @@
mCallbackHandler.dump(pw);
}
- private static final String emergencyToString(int emergencySource) {
+ private static String emergencyToString(int emergencySource) {
if (emergencySource > EMERGENCY_NO_SUB) {
return "ASSUMED_VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER)
+ ")";
@@ -1423,10 +1434,12 @@
return info;
}
+ /** */
public boolean hasEmergencyCryptKeeperText() {
return EncryptionHelper.IS_DATA_ENCRYPTED;
}
+ /** */
public boolean isRadioOn() {
return !mAirplaneMode;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
index 4b6722c..d23dba5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
-import static com.android.systemui.statusbar.policy.NetworkControllerImpl.TAG;
+import static com.android.systemui.statusbar.connectivity.NetworkControllerImpl.TAG;
import android.annotation.NonNull;
import android.content.Context;
@@ -23,8 +23,8 @@
import com.android.settingslib.SignalIcon.IconGroup;
import com.android.settingslib.SignalIcon.State;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import java.io.PrintWriter;
import java.util.BitSet;
@@ -83,7 +83,7 @@
return mCurrentState;
}
- public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
+ void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) {
mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0;
notifyListenersIfNecessary();
}
@@ -114,7 +114,7 @@
return false;
}
- public void saveLastState() {
+ void saveLastState() {
if (RECORD_HISTORY) {
recordLastState();
}
@@ -161,7 +161,7 @@
}
}
- public void notifyListenersIfNecessary() {
+ void notifyListenersIfNecessary() {
if (isDirty()) {
saveLastState();
notifyListeners();
@@ -192,7 +192,7 @@
mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
}
- public void dump(PrintWriter pw) {
+ void dump(PrintWriter pw) {
pw.println(" - " + mTag + " -----");
pw.println(" Current State: " + mCurrentState);
if (RECORD_HISTORY) {
@@ -210,7 +210,7 @@
}
}
- public final void notifyListeners() {
+ final void notifyListeners() {
notifyListeners(mCallbackHandler);
}
@@ -219,7 +219,7 @@
* based on current state, and only need to be called in the scenario where
* mCurrentState != mLastState.
*/
- public abstract void notifyListeners(SignalCallback callback);
+ abstract void notifyListeners(SignalCallback callback);
/**
* Generate a blank T.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java
index 577cc4f..3c449ad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.R;
import com.android.settingslib.SignalIcon.IconGroup;
+/** */
public class WifiIcons {
static final int[] WIFI_FULL_ICONS = {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 3f7ddc6..3622a66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN;
import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT;
@@ -37,14 +37,15 @@
import com.android.settingslib.wifi.WifiStatusTracker;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import java.io.PrintWriter;
import java.util.Objects;
+/** */
public class WifiSignalController extends
SignalController<WifiSignalController.WifiState, IconGroup> {
private final boolean mHasMobileDataFeature;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 94f186f..6730afa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -22,6 +22,9 @@
import android.os.Handler;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
@@ -62,6 +65,7 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
+import com.android.systemui.statusbar.phone.SystemUIHostDialogProvider;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
@@ -248,11 +252,12 @@
ActivityStarter activityStarter,
@Main Executor mainExecutor,
IActivityManager iActivityManager,
- OngoingCallLogger logger) {
+ OngoingCallLogger logger,
+ DumpManager dumpManager) {
OngoingCallController ongoingCallController =
new OngoingCallController(
notifCollection, featureFlags, systemClock, activityStarter, mainExecutor,
- iActivityManager, logger);
+ iActivityManager, logger, dumpManager);
ongoingCallController.init();
return ongoingCallController;
}
@@ -261,4 +266,29 @@
@Binds
QSCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
QSCarrierGroupController.SubscriptionManagerSlotIndexResolver impl);
+
+ /**
+ */
+ @Provides
+ @SysUISingleton
+ static LaunchAnimator provideLaunchAnimator(Context context) {
+ return new LaunchAnimator(context);
+ }
+
+ /**
+ */
+ @Provides
+ @SysUISingleton
+ static ActivityLaunchAnimator provideActivityLaunchAnimator(LaunchAnimator launchAnimator) {
+ return new ActivityLaunchAnimator(launchAnimator);
+ }
+
+ /**
+ */
+ @Provides
+ @SysUISingleton
+ static DialogLaunchAnimator provideDialogLaunchAnimator(Context context,
+ LaunchAnimator launchAnimator) {
+ return new DialogLaunchAnimator(context, launchAnimator, new SystemUIHostDialogProvider());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index f2060b7..83c98d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -45,6 +45,7 @@
import com.android.systemui.settings.UserTracker
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.util.concurrency.Execution
import com.android.systemui.util.settings.SecureSettings
import java.lang.RuntimeException
@@ -67,6 +68,7 @@
private val contentResolver: ContentResolver,
private val configurationController: ConfigurationController,
private val statusBarStateController: StatusBarStateController,
+ private val deviceProvisionedController: DeviceProvisionedController,
private val execution: Execution,
@Main private val uiExecutor: Executor,
@Main private val handler: Handler,
@@ -74,15 +76,81 @@
) {
private var session: SmartspaceSession? = null
private val plugin: BcSmartspaceDataPlugin? = optionalPlugin.orElse(null)
- private lateinit var smartspaceView: SmartspaceView
- lateinit var view: View
- private set
+ // Smartspace can be used on multiple displays, such as when the user casts their screen
+ private var smartspaceViews = mutableSetOf<SmartspaceView>()
private var showSensitiveContentForCurrentUser = false
private var showSensitiveContentForManagedUser = false
private var managedUserHandle: UserHandle? = null
+ var stateChangeListener = object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(v: View) {
+ smartspaceViews.add(v as SmartspaceView)
+ connectSession()
+
+ updateTextColorFromWallpaper()
+ statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount)
+ }
+
+ override fun onViewDetachedFromWindow(v: View) {
+ smartspaceViews.remove(v as SmartspaceView)
+
+ if (smartspaceViews.isEmpty()) {
+ disconnect()
+ }
+ }
+ }
+
+ private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
+ execution.assertIsMainThread()
+ val filteredTargets = targets.filter(::filterSmartspaceTarget)
+ plugin?.onTargetsAvailable(filteredTargets)
+ }
+
+ private val userTrackerCallback = object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ execution.assertIsMainThread()
+ reloadSmartspace()
+ }
+ }
+
+ private val settingsObserver = object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ execution.assertIsMainThread()
+ reloadSmartspace()
+ }
+ }
+
+ private val configChangeListener = object : ConfigurationController.ConfigurationListener {
+ override fun onThemeChanged() {
+ execution.assertIsMainThread()
+ updateTextColorFromWallpaper()
+ }
+ }
+
+ private val statusBarStateListener = object : StatusBarStateController.StateListener {
+ override fun onDozeAmountChanged(linear: Float, eased: Float) {
+ execution.assertIsMainThread()
+ smartspaceViews.forEach { it.setDozeAmount(eased) }
+ }
+ }
+
+ private val deviceProvisionedListener =
+ object : DeviceProvisionedController.DeviceProvisionedListener {
+ override fun onDeviceProvisionedChanged() {
+ connectSession()
+ }
+
+ override fun onUserSetupChanged() {
+ connectSession()
+ }
+ }
+
+ init {
+ deviceProvisionedController.addCallback(deviceProvisionedListener)
+ }
+
fun isEnabled(): Boolean {
execution.assertIsMainThread()
@@ -90,17 +158,16 @@
}
/**
- * Constructs the smartspace view and connects it to the smartspace service. Subsequent calls
- * are idempotent until [disconnect] is called.
+ * Constructs the smartspace view and connects it to the smartspace service.
*/
- fun buildAndConnectView(parent: ViewGroup): View {
+ fun buildAndConnectView(parent: ViewGroup): View? {
execution.assertIsMainThread()
if (!isEnabled()) {
throw RuntimeException("Cannot build view when not enabled")
}
- buildView(parent)
+ val view = buildView(parent)
connectSession()
return view
@@ -110,14 +177,9 @@
session?.requestSmartspaceUpdate()
}
- private fun buildView(parent: ViewGroup) {
+ private fun buildView(parent: ViewGroup): View? {
if (plugin == null) {
- return
- }
- if (this::view.isInitialized) {
- // Due to some oddities with a singleton smartspace view, allow reparenting
- (view.getParent() as ViewGroup?)?.removeView(view)
- return
+ return null
}
val ssView = plugin.getView(parent)
@@ -132,22 +194,27 @@
}
})
ssView.setFalsingManager(falsingManager)
-
- this.smartspaceView = ssView
- this.view = ssView as View
-
- updateTextColorFromWallpaper()
- statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount)
+ return (ssView as View).apply { addOnAttachStateChangeListener(stateChangeListener) }
}
private fun connectSession() {
if (plugin == null || session != null) {
return
}
- val session = smartspaceManager.createSmartspaceSession(
- SmartspaceConfig.Builder(context, "lockscreen").build())
- session.addOnTargetsAvailableListener(uiExecutor, sessionListener)
+ // Only connect after the device is fully provisioned to avoid connection caching
+ // issues
+ if (!deviceProvisionedController.isDeviceProvisioned() ||
+ !deviceProvisionedController.isCurrentUserSetup()) {
+ return
+ }
+
+ val newSession = smartspaceManager.createSmartspaceSession(
+ SmartspaceConfig.Builder(context, "lockscreen").build())
+ newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener)
+ this.session = newSession
+
+ deviceProvisionedController.removeCallback(deviceProvisionedListener)
userTracker.addCallback(userTrackerCallback, uiExecutor)
contentResolver.registerContentObserver(
secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
@@ -158,15 +225,11 @@
configurationController.addCallback(configChangeListener)
statusBarStateController.addCallback(statusBarStateListener)
- this.session = session
-
reloadSmartspace()
}
/**
* Disconnects the smartspace view from the smartspace service and cleans up any resources.
- * Calling [buildAndConnectView] again will cause the same view to be reconnected to the
- * service.
*/
fun disconnect() {
execution.assertIsMainThread()
@@ -198,43 +261,6 @@
plugin?.unregisterListener(listener)
}
- private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
- execution.assertIsMainThread()
- val filteredTargets = targets.filter(::filterSmartspaceTarget)
- plugin?.onTargetsAvailable(filteredTargets)
- }
-
- private val userTrackerCallback = object : UserTracker.Callback {
- override fun onUserChanged(newUser: Int, userContext: Context) {
- execution.assertIsMainThread()
- reloadSmartspace()
- }
-
- override fun onProfilesChanged(profiles: List<UserInfo>) {
- }
- }
-
- private val settingsObserver = object : ContentObserver(handler) {
- override fun onChange(selfChange: Boolean, uri: Uri?) {
- execution.assertIsMainThread()
- reloadSmartspace()
- }
- }
-
- private val configChangeListener = object : ConfigurationController.ConfigurationListener {
- override fun onThemeChanged() {
- execution.assertIsMainThread()
- updateTextColorFromWallpaper()
- }
- }
-
- private val statusBarStateListener = object : StatusBarStateController.StateListener {
- override fun onDozeAmountChanged(linear: Float, eased: Float) {
- execution.assertIsMainThread()
- smartspaceView.setDozeAmount(eased)
- }
- }
-
private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean {
return when (t.userHandle) {
userTracker.userHandle -> {
@@ -256,7 +282,7 @@
private fun updateTextColorFromWallpaper() {
val wallpaperTextColor = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor)
- smartspaceView.setPrimaryTextColor(wallpaperTextColor)
+ smartspaceViews.forEach { it.setPrimaryTextColor(wallpaperTextColor) }
}
private fun reloadSmartspace() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
index f19cf5d..64a7305 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ExpandAnimationParameters.kt
@@ -2,8 +2,8 @@
import android.util.MathUtils
import com.android.internal.annotations.VisibleForTesting
-import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.Interpolators
+import com.android.systemui.animation.LaunchAnimator
import kotlin.math.min
/** Parameters for the notifications expand animations. */
@@ -15,7 +15,7 @@
topCornerRadius: Float = 0f,
bottomCornerRadius: Float = 0f
-) : ActivityLaunchAnimator.State(top, bottom, left, right, topCornerRadius, bottomCornerRadius) {
+) : LaunchAnimator.State(top, bottom, left, right, topCornerRadius, bottomCornerRadius) {
@VisibleForTesting
constructor() : this(
top = 0, bottom = 0, left = 0, right = 0, topCornerRadius = 0f, bottomCornerRadius = 0f
@@ -55,6 +55,6 @@
}
fun getProgress(delay: Long, duration: Long): Float {
- return ActivityLaunchAnimator.getProgress(linearProgress, delay, duration)
+ return LaunchAnimator.getProgress(linearProgress, delay, duration)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index a9ad000..60f44a0d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -305,9 +305,6 @@
NotificationEntry entry = mPendingNotifications.get(key);
entry.abortTask();
mPendingNotifications.remove(key);
- for (NotifCollectionListener listener : mNotifCollectionListeners) {
- listener.onEntryCleanUp(entry);
- }
mLogger.logInflationAborted(key, "pending", reason);
}
NotificationEntry addedEntry = getActiveNotificationUnfiltered(key);
@@ -477,6 +474,18 @@
if (!lifetimeExtended) {
// At this point, we are guaranteed the notification will be removed
abortExistingInflation(key, "removeNotification");
+ // Fix for b/201097913: NotifCollectionListener#onEntryRemoved specifies that
+ // #onEntryRemoved should be called when a notification is cancelled,
+ // regardless of whether the notification was pending or active.
+ // Note that mNotificationEntryListeners are NOT notified of #onEntryRemoved
+ // because for that interface, #onEntryRemoved should only be called for
+ // active entries, NOT pending ones.
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryRemoved(pendingEntry, REASON_UNKNOWN);
+ }
+ for (NotifCollectionListener listener : mNotifCollectionListeners) {
+ listener.onEntryCleanUp(pendingEntry);
+ }
mAllNotifications.remove(pendingEntry);
mLeakDetector.trackGarbage(pendingEntry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 1bbef25..22c3eda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -3,6 +3,7 @@
import android.view.ViewGroup
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.LaunchAnimator
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
@@ -54,7 +55,7 @@
// Do nothing. Notifications are always animated inside their rootView.
}
- override fun createAnimatorState(): ActivityLaunchAnimator.State {
+ override fun createAnimatorState(): LaunchAnimator.State {
// If the notification panel is collapsed, the clip may be larger than the height.
val height = max(0, notification.actualHeight - notification.clipBottomAmount)
val location = notification.locationOnScreen
@@ -72,12 +73,12 @@
notification.currentBackgroundRadiusTop
}
val params = ExpandAnimationParameters(
- top = windowTop,
- bottom = location[1] + height,
- left = location[0],
- right = location[0] + notification.width,
- topCornerRadius = topCornerRadius,
- bottomCornerRadius = notification.currentBackgroundRadiusBottom
+ top = windowTop,
+ bottom = location[1] + height,
+ left = location[0],
+ right = location[0] + notification.width,
+ topCornerRadius = topCornerRadius,
+ bottomCornerRadius = notification.currentBackgroundRadiusBottom
)
params.startTranslationZ = notification.translationZ
@@ -86,8 +87,8 @@
params.startClipTopAmount = notification.clipTopAmount
if (notification.isChildInGroup) {
params.startNotificationTop += notification.notificationParent.translationY
- val parentRoundedClip = Math.max(clipStartLocation
- - notification.notificationParent.locationOnScreen[1], 0)
+ val parentRoundedClip = Math.max(
+ clipStartLocation - notification.notificationParent.locationOnScreen[1], 0)
params.parentStartRoundedTopClipping = parentRoundedClip
val parentClip = notification.notificationParent.clipTopAmount
@@ -157,7 +158,7 @@
}
override fun onLaunchAnimationProgress(
- state: ActivityLaunchAnimator.State,
+ state: LaunchAnimator.State,
progress: Float,
linearProgress: Float
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
index b3f8612..aeb2efd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
@@ -171,23 +171,4 @@
return false;
}
-
- /**
- * Injectable factory for creating a {@link AutoHideController}.
- */
- public static class Factory {
- private final Handler mHandler;
- private final IWindowManager mIWindowManager;
-
- @Inject
- public Factory(@Main Handler handler, IWindowManager iWindowManager) {
- mHandler = handler;
- mIWindowManager = iWindowManager;
- }
-
- /** Create an {@link AutoHideController} */
- public AutoHideController create(Context context) {
- return new AutoHideController(context, mHandler, mIWindowManager);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 1bac8fc..cfe044a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -46,6 +46,8 @@
import com.android.systemui.statusbar.OperatorNameView;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
@@ -53,8 +55,6 @@
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
import com.android.systemui.statusbar.policy.EncryptionHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import org.jetbrains.annotations.NotNull;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index a8c62fe..5bf982b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -22,6 +22,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.Log;
import android.util.MathUtils;
import androidx.annotation.NonNull;
@@ -35,6 +36,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.tuner.TunerService;
import java.io.FileDescriptor;
@@ -256,9 +258,20 @@
}
/**
+ * Whether the single tap sensor uses the proximity sensor for this device posture.
+ */
+ public boolean singleTapUsesProx(@DevicePostureController.DevicePostureInt int devicePosture) {
+ return getPostureSpecificBool(
+ mResources.getIntArray(R.array.doze_single_tap_uses_prox_posture_mapping),
+ singleTapUsesProx(),
+ devicePosture
+ );
+ }
+
+ /**
* Whether the single tap sensor uses the proximity sensor.
*/
- public boolean singleTapUsesProx() {
+ private boolean singleTapUsesProx() {
return mResources.getBoolean(R.bool.doze_single_tap_uses_prox);
}
@@ -270,6 +283,17 @@
}
/**
+ * Sensor to use for brightness changes.
+ */
+ public String brightnessName(@DevicePostureController.DevicePostureInt int posture) {
+ return AmbientDisplayConfiguration.getSensorFromPostureMapping(
+ mResources.getStringArray(R.array.doze_brightness_sensor_name_posture_mapping),
+ null /* defaultValue */,
+ posture
+ );
+ }
+
+ /**
* Callback to listen for DozeParameter changes.
*/
public void addCallback(Callback callback) {
@@ -308,6 +332,20 @@
pw.println(getSelectivelyRegisterSensorsUsingProx());
}
+ private boolean getPostureSpecificBool(
+ int[] postureMapping,
+ boolean defaultSensorBool,
+ int posture) {
+ boolean bool = defaultSensorBool;
+ if (posture < postureMapping.length) {
+ bool = postureMapping[posture] != 0;
+ } else {
+ Log.e("DozeParameters", "Unsupported doze posture " + posture);
+ }
+
+ return bool;
+ }
+
interface Callback {
/**
* Invoked when the value of getAlwaysOn may have changed.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 5f44a7d..fe154d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -72,6 +72,7 @@
private boolean mKeyguardUserSwitcherEnabled;
private final UserManager mUserManager;
+ private boolean mIsPrivacyDotEnabled;
private int mSystemIconsSwitcherHiddenExpandedMargin;
private int mStatusBarPaddingEnd;
private int mMinDotWidth;
@@ -112,7 +113,7 @@
mCutoutSpace = findViewById(R.id.cutout_space_view);
mStatusIconArea = findViewById(R.id.status_icon_area);
mStatusIconContainer = findViewById(R.id.statusIcons);
-
+ mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot);
loadDimens();
}
@@ -270,9 +271,10 @@
mDisplayCutout, cornerCutoutMargins, mRoundedCornerPadding);
// consider privacy dot space
- final int minLeft = isLayoutRtl() ? Math.max(mMinDotWidth, mPadding.first) : mPadding.first;
- final int minRight = isLayoutRtl() ? mPadding.second :
- Math.max(mMinDotWidth, mPadding.second);
+ final int minLeft = (isLayoutRtl() && mIsPrivacyDotEnabled)
+ ? Math.max(mMinDotWidth, mPadding.first) : mPadding.first;
+ final int minRight = (!isLayoutRtl() && mIsPrivacyDotEnabled)
+ ? Math.max(mMinDotWidth, mPadding.second) : mPadding.second;
setPadding(minLeft, waterfallTop, minRight, 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 893aa6d..5feb405 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -101,8 +101,8 @@
@Override
public void onOverlayChanged() {
- KeyguardStatusBarViewController.this.onThemeChanged();
mView.onOverlayChanged();
+ KeyguardStatusBarViewController.this.onThemeChanged();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
index 3679682..abee7a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarController.java
@@ -298,33 +298,4 @@
pw.println();
}
}
-
- /**
- * Injectable factory for creating a {@link LightBarController}.
- */
- public static class Factory {
- private final DarkIconDispatcher mDarkIconDispatcher;
- private final BatteryController mBatteryController;
- private final NavigationModeController mNavModeController;
- private final DumpManager mDumpManager;
-
- @Inject
- public Factory(
- DarkIconDispatcher darkIconDispatcher,
- BatteryController batteryController,
- NavigationModeController navModeController,
- DumpManager dumpManager) {
-
- mDarkIconDispatcher = darkIconDispatcher;
- mBatteryController = batteryController;
- mNavModeController = navModeController;
- mDumpManager = dumpManager;
- }
-
- /** Create an {@link LightBarController} */
- public LightBarController create(Context context) {
- return new LightBarController(context, mDarkIconDispatcher, mBatteryController,
- mNavModeController, mDumpManager);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index dd6b1c7..2e7f4b8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -104,8 +104,8 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
@@ -225,7 +225,7 @@
*/
private static final int FLING_HIDE = 2;
private static final long ANIMATION_DELAY_ICON_FADE_IN =
- ActivityLaunchAnimator.ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION
+ LaunchAnimator.ANIMATION_DURATION - CollapsedStatusBarFragment.FADE_IN_DURATION
- CollapsedStatusBarFragment.FADE_IN_DELAY - 48;
private final DozeParameters mDozeParameters;
@@ -323,7 +323,6 @@
private KeyguardUserSwitcherController mKeyguardUserSwitcherController;
private KeyguardStatusBarView mKeyguardStatusBar;
private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
- private ViewGroup mBigClockContainer;
@VisibleForTesting QS mQs;
private FrameLayout mQsFrame;
@Nullable
@@ -358,6 +357,7 @@
private boolean mKeyguardUserSwitcherEnabled;
private boolean mDozing;
private boolean mDozingOnDown;
+ private boolean mBouncerShowing;
private int mBarState;
private float mInitialHeightOnTouch;
private float mInitialTouchX;
@@ -434,7 +434,7 @@
private Runnable mHeadsUpExistenceChangedRunnable = () -> {
setHeadsUpAnimatingAway(false);
- notifyBarPanelExpansionChanged();
+ updatePanelExpansionAndVisibility();
};
// TODO (b/162832756): once migrated to the new pipeline, delete legacy group manager
private NotificationGroupManagerLegacy mGroupManager;
@@ -885,7 +885,6 @@
private void onFinishInflate() {
loadDimens();
mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
- mBigClockContainer = mView.findViewById(R.id.big_clock_container);
mCommunalView = mView.findViewById(R.id.communal_host);
FrameLayout userAvatarContainer = null;
@@ -1192,7 +1191,6 @@
R.layout.keyguard_user_switcher /* layoutId */,
showKeyguardUserSwitcher /* enabled */);
- mBigClockContainer.removeAllViews();
updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
keyguardUserSwitcherView, mView.findViewById(R.id.idle_host_view), mCommunalView);
@@ -1385,8 +1383,10 @@
boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled();
final boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
.getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
+ boolean splitShadeWithActiveMedia =
+ mShouldUseSplitNotificationShade && mMediaDataManager.hasActiveMedia();
if ((hasVisibleNotifications && !mShouldUseSplitNotificationShade)
- || (mShouldUseSplitNotificationShade && mMediaDataManager.hasActiveMedia())) {
+ || (splitShadeWithActiveMedia && !mDozing)) {
mKeyguardStatusViewController.displayClock(SMALL);
} else {
mKeyguardStatusViewController.displayClock(LARGE);
@@ -1450,7 +1450,7 @@
.getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
boolean hasCommunalSurface = mCommunalSource != null && mCommunalSource.get() != null;
boolean shouldBeCentered = !mShouldUseSplitNotificationShade
- || (!hasVisibleNotifications && !hasCommunalSurface);
+ || (!hasVisibleNotifications && !hasCommunalSurface) || mDozing;
if (mStatusViewCentered != shouldBeCentered) {
mStatusViewCentered = shouldBeCentered;
ConstraintSet constraintSet = new ConstraintSet();
@@ -2323,7 +2323,6 @@
if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == KEYGUARD) {
updateKeyguardBottomAreaAlpha();
positionClockAndNotifications();
- updateBigClockAlpha();
}
if (mAccessibilityManager.isEnabled()) {
@@ -3105,20 +3104,6 @@
mLockIconViewController.setAlpha(alpha);
}
- /**
- * Custom clock fades away when user drags up to unlock or pulls down quick settings.
- *
- * Updates alpha of custom clock to match the alpha of the KeyguardBottomArea. See
- * {@link #updateKeyguardBottomAreaAlpha}.
- */
- private void updateBigClockAlpha() {
- float expansionAlpha = MathUtils.map(
- isUnlockHintRunning() ? 0 : KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1f, 0f, 1f,
- getExpandedFraction());
- float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction());
- mBigClockContainer.setAlpha(alpha);
- }
-
@Override
protected void onExpandingStarted() {
super.onExpandingStarted();
@@ -3380,11 +3365,19 @@
public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) {
mHeadsUpAnimatingAway = headsUpAnimatingAway;
mNotificationStackScrollLayoutController.setHeadsUpAnimatingAway(headsUpAnimatingAway);
- updateHeadsUpVisibility();
+ updateVisibility();
}
- private void updateHeadsUpVisibility() {
- ((PhoneStatusBarView) mBar).setHeadsUpVisible(mHeadsUpAnimatingAway || mHeadsUpPinnedMode);
+ /** Set whether the bouncer is showing. */
+ public void setBouncerShowing(boolean bouncerShowing) {
+ mBouncerShowing = bouncerShowing;
+ updateVisibility();
+ }
+
+ @Override
+ protected boolean shouldPanelBeVisible() {
+ boolean headsUpVisible = mHeadsUpAnimatingAway || mHeadsUpPinnedMode;
+ return headsUpVisible || isExpanded() || mBouncerShowing;
}
@Override
@@ -3468,7 +3461,6 @@
}
mNotificationStackScrollLayoutController.setExpandedHeight(expandedHeight);
updateKeyguardBottomAreaAlpha();
- updateBigClockAlpha();
updateStatusBarIcons();
}
@@ -3746,7 +3738,7 @@
}
public void applyLaunchAnimationProgress(float linearProgress) {
- boolean hideIcons = ActivityLaunchAnimator.getProgress(linearProgress,
+ boolean hideIcons = LaunchAnimator.getProgress(linearProgress,
ANIMATION_DELAY_ICON_FADE_IN, 100) == 0.0f;
if (hideIcons != mHideIconsDuringLaunchAnimation) {
mHideIconsDuringLaunchAnimation = hideIcons;
@@ -3794,6 +3786,11 @@
}
}
+ /** */
+ public void setImportantForAccessibility(int mode) {
+ mView.setImportantForAccessibility(mode);
+ }
+
/**
* Do not let the user drag the shade up and down for the current touch session.
* This is necessary to avoid shade expansion while/after the bouncer is dismissed.
@@ -4369,7 +4366,7 @@
}
updateGestureExclusionRect();
mHeadsUpPinnedMode = inPinnedMode;
- updateHeadsUpVisibility();
+ updateVisibility();
mKeyguardStatusBarViewController.updateForHeadsUp();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index e775e96..247ede9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -33,8 +33,6 @@
private static final boolean SPEW = false;
private static final String PANEL_BAR_SUPER_PARCELABLE = "panel_bar_super_parcelable";
private static final String STATE = "state";
- private boolean mBouncerShowing;
- private boolean mExpanded;
protected float mPanelFraction;
public static final void LOG(String fmt, Object... args) {
@@ -99,33 +97,6 @@
pv.setBar(this);
}
- public void setBouncerShowing(boolean showing) {
- mBouncerShowing = showing;
- int important = showing ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-
- setImportantForAccessibility(important);
- updateVisibility();
-
- if (mPanel != null) mPanel.getView().setImportantForAccessibility(important);
- }
-
- public float getExpansionFraction() {
- return mPanelFraction;
- }
-
- public boolean isExpanded() {
- return mExpanded;
- }
-
- protected void updateVisibility() {
- mPanel.getView().setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
- }
-
- protected boolean shouldPanelBeVisible() {
- return mExpanded || mBouncerShowing;
- }
-
public boolean panelEnabled() {
return true;
}
@@ -183,9 +154,7 @@
boolean fullyClosed = true;
boolean fullyOpened = false;
if (SPEW) LOG("panelExpansionChanged: start state=%d, f=%.1f", mState, frac);
- mExpanded = expanded;
mPanelFraction = frac;
- updateVisibility();
// adjust any other panels that may be partially visible
if (expanded) {
if (mState == STATE_CLOSED) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 51cae8c..b215515 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.phone;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
import static com.android.systemui.classifier.Classifier.GENERIC;
@@ -315,7 +318,7 @@
}
private void startOpening(MotionEvent event) {
- notifyBarPanelExpansionChanged();
+ updatePanelExpansionAndVisibility();
maybeVibrateOnOpening();
//TODO: keyguard opens QS a different way; log that too?
@@ -447,7 +450,7 @@
protected void onTrackingStopped(boolean expand) {
mTracking = false;
mBar.onTrackingStopped(expand);
- notifyBarPanelExpansionChanged();
+ updatePanelExpansionAndVisibility();
}
protected void onTrackingStarted() {
@@ -455,7 +458,7 @@
mTracking = true;
mBar.onTrackingStarted();
notifyExpandingStarted();
- notifyBarPanelExpansionChanged();
+ updatePanelExpansionAndVisibility();
}
/**
@@ -685,7 +688,7 @@
} else {
cancelJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
}
- notifyBarPanelExpansionChanged();
+ updatePanelExpansionAndVisibility();
}
protected abstract boolean shouldUseDismissingAnimation();
@@ -760,7 +763,7 @@
mExpandedFraction = Math.min(1f,
maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight);
onHeightUpdated(mExpandedHeight);
- notifyBarPanelExpansionChanged();
+ updatePanelExpansionAndVisibility();
}
/**
@@ -878,7 +881,7 @@
if (mExpanding) {
notifyExpandingFinished();
}
- notifyBarPanelExpansionChanged();
+ updatePanelExpansionAndVisibility();
// Wait for window manager to pickup the change, so we know the maximum height of the panel
// then.
@@ -916,7 +919,7 @@
}
if (mInstantExpanding) {
mInstantExpanding = false;
- notifyBarPanelExpansionChanged();
+ updatePanelExpansionAndVisibility();
}
}
@@ -1022,7 +1025,7 @@
public void onAnimationEnd(Animator animation) {
setAnimator(null);
onAnimationFinished.run();
- notifyBarPanelExpansionChanged();
+ updatePanelExpansionAndVisibility();
}
});
animator.start();
@@ -1059,19 +1062,39 @@
return animator;
}
- protected void notifyBarPanelExpansionChanged() {
+ /** Update the visibility of {@link PanelView} if necessary. */
+ public void updateVisibility() {
+ mView.setVisibility(shouldPanelBeVisible() ? VISIBLE : INVISIBLE);
+ }
+
+ /** Returns true if {@link PanelView} should be visible. */
+ abstract boolean shouldPanelBeVisible();
+
+ /**
+ * Updates the panel expansion and {@link PanelView} visibility if necessary.
+ *
+ * TODO(b/200063118): Could public calls to this method be replaced with calls to
+ * {@link #updateVisibility()}? That would allow us to make this method private.
+ */
+ public void updatePanelExpansionAndVisibility() {
if (mBar != null) {
- mBar.panelExpansionChanged(
- mExpandedFraction,
- mExpandedFraction > 0f || mInstantExpanding
- || isPanelVisibleBecauseOfHeadsUp() || mTracking
- || mHeightAnimator != null && !mIsSpringBackAnimation);
+ mBar.panelExpansionChanged(mExpandedFraction, isExpanded());
}
+ updateVisibility();
for (int i = 0; i < mExpansionListeners.size(); i++) {
mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking);
}
}
+ public boolean isExpanded() {
+ return mExpandedFraction > 0f
+ || mInstantExpanding
+ || isPanelVisibleBecauseOfHeadsUp()
+ || mTracking
+ || mHeightAnimator != null
+ && !mIsSpringBackAnimation;
+ }
+
public void addExpansionListener(PanelExpansionListener panelExpansionListener) {
mExpansionListeners.add(panelExpansionListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index af556a2..9ab6cdd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -402,16 +402,6 @@
getPaddingBottom());
}
- public void setHeadsUpVisible(boolean headsUpVisible) {
- mHeadsUpVisible = headsUpVisible;
- updateVisibility();
- }
-
- @Override
- protected boolean shouldPanelBeVisible() {
- return mHeadsUpVisible || super.shouldPanelBeVisible();
- }
-
/** An interface that will provide whether panel is enabled. */
interface PanelEnabledProvider {
/** Returns true if the panel is enabled and false otherwise. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 28040fd..4c0332a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -61,6 +61,10 @@
}
}
+ fun setImportantForAccessibility(mode: Int) {
+ mView.importantForAccessibility = mode
+ }
+
private class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider {
override fun getViewCenter(view: View, outPoint: Point) =
when (view.id) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 4213902..a564637 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -1230,6 +1230,8 @@
pw.println(mDefaultScrimAlpha);
pw.print(" mExpansionFraction=");
pw.println(mPanelExpansion);
+ pw.print(" mExpansionAffectsAlpha=");
+ pw.println(mExpansionAffectsAlpha);
pw.print(" mState.getMaxLightRevealScrimAlpha=");
pw.println(mState.getMaxLightRevealScrimAlpha());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 865dbad..06c3573 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -26,6 +26,8 @@
import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
+import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+import static androidx.core.view.ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
import static androidx.lifecycle.Lifecycle.State.RESUMED;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
@@ -135,6 +137,7 @@
import com.android.systemui.SystemUI;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.DelegateLaunchAnimatorController;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.biometrics.AuthRippleController;
@@ -196,6 +199,7 @@
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -223,7 +227,6 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.tuner.TunerService;
@@ -669,7 +672,8 @@
private final SysuiStatusBarStateController mStatusBarStateController;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
- private ActivityLaunchAnimator mActivityLaunchAnimator;
+ private final ActivityLaunchAnimator mActivityLaunchAnimator;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
protected StatusBarNotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
@@ -792,7 +796,9 @@
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
Optional<StartingSurface> startingSurfaceOptional,
TunerService tunerService,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ ActivityLaunchAnimator activityLaunchAnimator,
+ DialogLaunchAnimator dialogLaunchAnimator) {
super(context);
mNotificationsController = notificationsController;
mLightBarController = lightBarController;
@@ -900,6 +906,8 @@
});
mActivityIntentHelper = new ActivityIntentHelper(mContext);
+ mActivityLaunchAnimator = activityLaunchAnimator;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
// TODO(b/190746471): Find a better home for this.
DateTimeView.setReceiverHandler(timeTickHandler);
@@ -1139,6 +1147,8 @@
mNotificationPanelViewController.addExpansionListener(
this::dispatchPanelExpansionForKeyguardDismiss);
+ mUserSwitcherController.init(mNotificationShadeWindowView);
+
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
mPluginDependencyProvider.allowPluginDependency(DarkIconDispatcher.class);
mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);
@@ -1180,22 +1190,13 @@
);
mBatteryMeterViewController.init();
- // CollapsedStatusBarFragment re-inflated PhoneStatusBarView and both of
- // mStatusBarView.mExpanded and mStatusBarView.mBouncerShowing are false.
- // PhoneStatusBarView's new instance will set to be gone in
- // PanelBar.updateVisibility after calling mStatusBarView.setBouncerShowing
- // that will trigger PanelBar.updateVisibility. If there is a heads up showing,
- // it needs to notify PhoneStatusBarView's new instance to update the correct
- // status by calling mNotificationPanel.notifyBarPanelExpansionChanged().
- if (mHeadsUpManager.hasPinnedHeadsUp()) {
- mNotificationPanelViewController.notifyBarPanelExpansionChanged();
- }
- mStatusBarView.setBouncerShowing(mBouncerShowing);
- if (oldStatusBarView != null) {
- float fraction = oldStatusBarView.getExpansionFraction();
- boolean expanded = oldStatusBarView.isExpanded();
- mStatusBarView.panelExpansionChanged(fraction, expanded);
- }
+ // Ensure we re-propagate panel expansion values to the panel controller and
+ // any listeners it may have, such as PanelBar. This will also ensure we
+ // re-display the notification panel if necessary (for example, if
+ // a heads-up notification was being displayed and should continue being
+ // displayed).
+ mNotificationPanelViewController.updatePanelExpansionAndVisibility();
+ setBouncerShowingForStatusBarComponents(mBouncerShowing);
HeadsUpAppearanceController oldController = mHeadsUpAppearanceController;
if (mHeadsUpAppearanceController != null) {
@@ -1467,7 +1468,7 @@
private void setUpPresenter() {
// Set up the initial notification state.
- mActivityLaunchAnimator = new ActivityLaunchAnimator(mKeyguardHandler, mContext);
+ mActivityLaunchAnimator.setCallback(mKeyguardHandler);
mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
mNotificationShadeWindowViewController,
mStackScrollerController.getNotificationListContainer(),
@@ -2549,7 +2550,8 @@
animationController != null && !willLaunchResolverActivity && shouldAnimateLaunch(
true /* isActivityIntent */);
ActivityLaunchAnimator.Controller animController =
- animate ? wrapAnimationController(animationController, dismissShade) : null;
+ animationController != null ? wrapAnimationController(animationController,
+ dismissShade) : null;
// If we animate, we will dismiss the shade only once the animation is done. This is taken
// care of by the StatusBarLaunchAnimationController.
@@ -3522,7 +3524,7 @@
mBouncerShowing = bouncerShowing;
mKeyguardBypassController.setBouncerShowing(bouncerShowing);
mPulseExpansionHandler.setBouncerShowing(bouncerShowing);
- if (mStatusBarView != null) mStatusBarView.setBouncerShowing(bouncerShowing);
+ setBouncerShowingForStatusBarComponents(bouncerShowing);
updateHideIconsForBouncer(true /* animate */);
mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
updateScrimController();
@@ -3532,6 +3534,23 @@
}
/**
+ * Propagate the bouncer state to status bar components.
+ *
+ * Separate from {@link #setBouncerShowing} because we sometimes re-create the status bar and
+ * should update only the status bar components.
+ */
+ private void setBouncerShowingForStatusBarComponents(boolean bouncerShowing) {
+ int importance = bouncerShowing
+ ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+ : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+ if (mPhoneStatusBarViewController != null) {
+ mPhoneStatusBarViewController.setImportantForAccessibility(importance);
+ }
+ mNotificationPanelViewController.setImportantForAccessibility(importance);
+ mNotificationPanelViewController.setBouncerShowing(bouncerShowing);
+ }
+
+ /**
* Collapses the notification shade if it is tracking or expanded.
*/
public void collapseShade() {
@@ -3775,8 +3794,11 @@
|| mKeyguardStateController.isKeyguardFadingAway();
// Do not animate the scrim expansion when triggered by the fingerprint sensor.
- mScrimController.setExpansionAffectsAlpha(
- !mBiometricUnlockController.isBiometricUnlock());
+ boolean onKeyguardOrHidingIt = mKeyguardStateController.isShowing()
+ || mKeyguardStateController.isKeyguardFadingAway()
+ || mKeyguardStateController.isKeyguardGoingAway();
+ mScrimController.setExpansionAffectsAlpha(!(mBiometricUnlockController.isBiometricUnlock()
+ && onKeyguardOrHidingIt));
boolean launchingAffordanceWithPreview =
mNotificationPanelViewController.isLaunchingAffordanceWithPreview();
@@ -4200,10 +4222,9 @@
}
private void sendInitialExpansionAmount(ExpansionChangedListener expansionChangedListener) {
- if (mStatusBarView != null) {
- expansionChangedListener.onExpansionChanged(mStatusBarView.getExpansionFraction(),
- mStatusBarView.isExpanded());
- }
+ expansionChangedListener.onExpansionChanged(
+ mNotificationPanelViewController.getExpandedFraction(),
+ mNotificationPanelViewController.isExpanded());
}
public void removeExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
@@ -4426,6 +4447,8 @@
&& !mBiometricUnlockController.isWakeAndUnlock()) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
+
+ mDialogLaunchAnimator.onDozeAmountChanged(linear);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 515094b..61552f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -70,6 +70,9 @@
// (e.g. network displays)
private val insetsCache = LruCache<CacheKey, Rect>(MAX_CACHE_SIZE)
private val listeners = mutableSetOf<StatusBarContentInsetsChangedListener>()
+ private val isPrivacyDotEnabled: Boolean by lazy(LazyThreadSafetyMode.PUBLICATION) {
+ context.resources.getBoolean(R.bool.config_enablePrivacyDot)
+ }
init {
configurationController.addCallback(this)
@@ -152,8 +155,9 @@
val isRtl = rotatedResources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
val roundedCornerPadding = rotatedResources
.getDimensionPixelSize(R.dimen.rounded_corner_content_padding)
- val minDotWidth = rotatedResources
- .getDimensionPixelSize(R.dimen.ongoing_appops_dot_min_padding)
+ val minDotWidth = if (isPrivacyDotEnabled)
+ rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_min_padding)
+ else 0
val minLeft: Int
val minRight: Int
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 7725443..5bc97a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -53,6 +53,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shared.system.QuickStepContract;
@@ -94,7 +95,8 @@
// with the appear animations of the PIN/pattern/password views.
private static final long NAV_BAR_SHOW_DELAY_BOUNCER = 320;
- private static final long WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS = 200;
+ // The duration to fade the nav bar content in/out when the device starts to sleep
+ private static final long NAV_BAR_CONTENT_FADE_DURATION = 125;
// Duration of the Keyguard dismissal animation in case the user is currently locked. This is to
// make everything a bit slower to bridge a gap until the user is unlocked and home screen has
@@ -197,10 +199,8 @@
private boolean mLastGesturalNav;
private boolean mLastIsDocked;
private boolean mLastPulsing;
- private boolean mLastAnimatedToSleep;
private int mLastBiometricMode;
private boolean mQsExpanded;
- private boolean mAnimatedToSleep;
private OnDismissAction mAfterKeyguardGoneAction;
private Runnable mKeyguardGoneCancelAction;
@@ -325,20 +325,6 @@
mDockManager.addListener(mDockEventListener);
mIsDocked = mDockManager.isDocked();
}
- mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() {
- @Override
- public void onFinishedWakingUp() {
- mAnimatedToSleep = false;
- updateStates();
- }
-
- @Override
- public void onFinishedGoingToSleep() {
- mAnimatedToSleep =
- mUnlockedScreenOffAnimationController.isScreenOffAnimationPlaying();
- updateStates();
- }
- });
}
@Override
@@ -571,12 +557,26 @@
public void onStartedWakingUp() {
mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(false);
+ View currentView = getCurrentNavBarView();
+ if (currentView != null) {
+ currentView.animate()
+ .alpha(1f)
+ .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
+ .start();
+ }
}
@Override
public void onStartedGoingToSleep() {
mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(true);
+ View currentView = getCurrentNavBarView();
+ if (currentView != null) {
+ currentView.animate()
+ .alpha(0f)
+ .setDuration(NAV_BAR_CONTENT_FADE_DURATION)
+ .start();
+ }
}
@Override
@@ -1013,10 +1013,28 @@
mLastBiometricMode = mBiometricUnlockController.getMode();
mLastGesturalNav = mGesturalNav;
mLastIsDocked = mIsDocked;
- mLastAnimatedToSleep = mAnimatedToSleep;
mStatusBar.onKeyguardViewManagerStatesUpdated();
}
+ /**
+ * Updates the visibility of the nav bar content views.
+ */
+ private void updateNavigationBarContentVisibility(boolean navBarContentVisible) {
+ final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ if (navBarView != null && navBarView.getCurrentView() != null) {
+ final View currentView = navBarView.getCurrentView();
+ currentView.setVisibility(navBarContentVisible ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+
+ private View getCurrentNavBarView() {
+ final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ return navBarView != null ? navBarView.getCurrentView() : null;
+ }
+
+ /**
+ * Updates the visibility of the nav bar window (which will cause insets changes).
+ */
protected void updateNavigationBarVisibility(boolean navBarVisible) {
if (mStatusBar.getNavigationBarView() != null) {
if (navBarVisible) {
@@ -1044,7 +1062,7 @@
boolean hideWhileDozing = mDozing && biometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
boolean keyguardWithGestureNav = (keyguardShowing && !mDozing || mPulsing && !mIsDocked)
&& mGesturalNav;
- return (!mAnimatedToSleep && !keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
+ return (!keyguardShowing && !hideWhileDozing || mBouncer.isShowing()
|| mRemoteInputActive || keyguardWithGestureNav
|| mGlobalActionsVisible);
}
@@ -1057,7 +1075,7 @@
boolean hideWhileDozing = mLastDozing && mLastBiometricMode != MODE_WAKE_AND_UNLOCK_PULSING;
boolean keyguardWithGestureNav = (keyguardShowing && !mLastDozing
|| mLastPulsing && !mLastIsDocked) && mLastGesturalNav;
- return (!mLastAnimatedToSleep && !keyguardShowing && !hideWhileDozing || mLastBouncerShowing
+ return (!keyguardShowing && !hideWhileDozing || mLastBouncerShowing
|| mLastRemoteInputActive || keyguardWithGestureNav
|| mLastGlobalActionsVisible);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index 14e513a..32aae6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -1,6 +1,7 @@
package com.android.systemui.statusbar.phone
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.LaunchAnimator
/**
* A [ActivityLaunchAnimator.Controller] that takes care of collapsing the status bar at the right
@@ -22,7 +23,7 @@
delegate.onLaunchAnimationStart(isExpandingFullyAbove)
statusBar.notificationPanelViewController.setIsLaunchAnimationRunning(true)
if (!isExpandingFullyAbove) {
- statusBar.collapsePanelWithDuration(ActivityLaunchAnimator.ANIMATION_DURATION.toInt())
+ statusBar.collapsePanelWithDuration(LaunchAnimator.ANIMATION_DURATION.toInt())
}
}
@@ -33,7 +34,7 @@
}
override fun onLaunchAnimationProgress(
- state: ActivityLaunchAnimator.State,
+ state: LaunchAnimator.State,
progress: Float,
linearProgress: Float
) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 7136432..fbd9ef7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -26,11 +26,11 @@
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 1e98c75..9415d50 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -30,19 +30,24 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogListener;
+import com.android.systemui.animation.ListenableDialog;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
/**
* Base class for dialogs that should appear over panels and keyguard.
* The SystemUIDialog registers a listener for the screen off / close system dialogs broadcast,
* and dismisses itself when it receives the broadcast.
*/
-public class SystemUIDialog extends AlertDialog {
-
+public class SystemUIDialog extends AlertDialog implements ListenableDialog {
private final Context mContext;
private final DismissReceiver mDismissReceiver;
+ private final Set<DialogListener> mDialogListeners = new LinkedHashSet<>();
public SystemUIDialog(Context context) {
this(context, R.style.Theme_SystemUI_Dialog);
@@ -72,6 +77,43 @@
mDismissReceiver.unregister();
}
+ @Override
+ public void addListener(DialogListener listener) {
+ mDialogListeners.add(listener);
+ }
+
+ @Override
+ public void removeListener(DialogListener listener) {
+ mDialogListeners.remove(listener);
+ }
+
+ @Override
+ public void dismiss() {
+ super.dismiss();
+
+ for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
+ listener.onDismiss();
+ }
+ }
+
+ @Override
+ public void hide() {
+ super.hide();
+
+ for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
+ listener.onHide();
+ }
+ }
+
+ @Override
+ public void show() {
+ super.show();
+
+ for (DialogListener listener : new LinkedHashSet<>(mDialogListeners)) {
+ listener.onShow();
+ }
+ }
+
public void setShowForAllUsers(boolean show) {
setShowForAllUsers(this, show);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt
new file mode 100644
index 0000000..6a49a6d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIHostDialogProvider.kt
@@ -0,0 +1,36 @@
+package com.android.systemui.statusbar.phone
+
+import android.app.Dialog
+import android.content.Context
+import android.os.Bundle
+import com.android.systemui.animation.HostDialogProvider
+
+/** An implementation of [HostDialogProvider] to be used when animating SysUI dialogs. */
+class SystemUIHostDialogProvider : HostDialogProvider {
+ override fun createHostDialog(
+ context: Context,
+ theme: Int,
+ onCreateCallback: () -> Unit,
+ dismissOverride: (() -> Unit) -> Unit
+ ): Dialog {
+ return SystemUIHostDialog(context, theme, onCreateCallback, dismissOverride)
+ }
+
+ private class SystemUIHostDialog(
+ context: Context,
+ theme: Int,
+ private val onCreateCallback: () -> Unit,
+ private val dismissOverride: (() -> Unit) -> Unit
+ ) : SystemUIDialog(context, theme) {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ onCreateCallback()
+ }
+
+ override fun dismiss() {
+ dismissOverride {
+ super.dismiss()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 13eb75a..5689707 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -28,6 +28,8 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.InitController;
+import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
@@ -60,6 +62,7 @@
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -102,7 +105,6 @@
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.tuner.TunerService;
@@ -235,7 +237,9 @@
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
Optional<StartingSurface> startingSurfaceOptional,
TunerService tunerService,
- DumpManager dumpManager) {
+ DumpManager dumpManager,
+ ActivityLaunchAnimator activityLaunchAnimator,
+ DialogLaunchAnimator dialogLaunchAnimator) {
return new StatusBar(
context,
notificationsController,
@@ -333,6 +337,8 @@
unlockedScreenOffAnimationController,
startingSurfaceOptional,
tunerService,
- dumpManager);
+ dumpManager,
+ activityLaunchAnimator,
+ dialogLaunchAnimator);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index eeff010..6fdf036 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -26,10 +26,12 @@
import android.view.View
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -37,6 +39,8 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.util.time.SystemClock
+import java.io.FileDescriptor
+import java.io.PrintWriter
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -51,13 +55,14 @@
private val activityStarter: ActivityStarter,
@Main private val mainExecutor: Executor,
private val iActivityManager: IActivityManager,
- private val logger: OngoingCallLogger
-) : CallbackController<OngoingCallListener> {
+ private val logger: OngoingCallLogger,
+ private val dumpManager: DumpManager,
+) : CallbackController<OngoingCallListener>, Dumpable {
/** Non-null if there's an active call notification. */
private var callNotificationInfo: CallNotificationInfo? = null
/** True if the application managing the call is visible to the user. */
- private var isCallAppVisible: Boolean = true
+ private var isCallAppVisible: Boolean = false
private var chipView: View? = null
private var uidObserver: IUidObserver.Stub? = null
@@ -120,6 +125,7 @@
}
fun init() {
+ dumpManager.registerDumpable(this)
if (featureFlags.isOngoingCallStatusBarChipEnabled) {
notifCollection.addCollectionListener(notifListener)
}
@@ -299,6 +305,11 @@
*/
fun hasValidStartTime(): Boolean = callStartTime > 0
}
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ pw.println("Active call notification: $callNotificationInfo")
+ pw.println("Call app visible: $isCallAppVisible")
+ }
}
private fun isCallNotification(entry: NotificationEntry): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
index 2dd4128..856853d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java
@@ -14,6 +14,7 @@
package com.android.systemui.statusbar.policy;
+import android.content.Context;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
@@ -34,8 +35,8 @@
private final AccessibilityManager mAccessibilityManager;
@Inject
- public AccessibilityManagerWrapper(AccessibilityManager accessibilityManager) {
- mAccessibilityManager = accessibilityManager;
+ public AccessibilityManagerWrapper(Context context) {
+ mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
index fbfa5e5..8596875 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DevicePostureController.java
@@ -50,6 +50,26 @@
/** Return the current device posture. */
@DevicePostureInt int getDevicePosture();
+ /**
+ * String representation of DevicePostureInt.
+ */
+ static String devicePostureToString(@DevicePostureInt int posture) {
+ switch (posture) {
+ case DEVICE_POSTURE_CLOSED:
+ return "DEVICE_POSTURE_CLOSED";
+ case DEVICE_POSTURE_HALF_OPENED:
+ return "DEVICE_POSTURE_HALF_OPENED";
+ case DEVICE_POSTURE_OPENED:
+ return "DEVICE_POSTURE_OPENED";
+ case DEVICE_POSTURE_FLIPPED:
+ return "DEVICE_POSTURE_FLIPPED";
+ case DEVICE_POSTURE_UNKNOWN:
+ return "DEVICE_POSTURE_UNKNOWN";
+ default:
+ return "UNSUPPORTED POSTURE posture=" + posture;
+ }
+ }
+
/** Callback to be notified about device posture changes. */
interface Callback {
/** Called when the posture changes. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 22f08ad..dadc016 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -25,6 +25,7 @@
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.app.admin.DevicePolicyManager;
import android.content.BroadcastReceiver;
@@ -55,6 +56,7 @@
import android.widget.BaseAdapter;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -75,6 +77,7 @@
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.qs.tiles.UserDetailView;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.CreateUserActivity;
@@ -107,6 +110,7 @@
private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000;
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+ private static final long MULTI_USER_JOURNEY_TIMEOUT = 20000l;
protected final Context mContext;
protected final UserTracker mUserTracker;
@@ -123,6 +127,7 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private final TelephonyListenerManager mTelephonyListenerManager;
private final IActivityTaskManager mActivityTaskManager;
+ private final InteractionJankMonitor mInteractionJankMonitor;
private ArrayList<UserRecord> mUsers = new ArrayList<>();
@VisibleForTesting
@@ -141,15 +146,18 @@
private Intent mSecondaryUserServiceIntent;
private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
private final UiEventLogger mUiEventLogger;
+ private final IActivityManager mActivityManager;
public final DetailAdapter mUserDetailAdapter;
private final Executor mBgExecutor;
private final boolean mGuestUserAutoCreated;
private final AtomicBoolean mGuestIsResetting;
private final AtomicBoolean mGuestCreationScheduled;
private FalsingManager mFalsingManager;
+ private NotificationShadeWindowView mRootView;
@Inject
public UserSwitcherController(Context context,
+ IActivityManager activityManager,
UserManager userManager,
UserTracker userTracker,
KeyguardStateController keyguardStateController,
@@ -165,14 +173,17 @@
UserDetailAdapter userDetailAdapter,
SecureSettings secureSettings,
@Background Executor bgExecutor,
+ InteractionJankMonitor interactionJankMonitor,
DumpManager dumpManager) {
mContext = context;
+ mActivityManager = activityManager;
mUserTracker = userTracker;
mBroadcastDispatcher = broadcastDispatcher;
mTelephonyListenerManager = telephonyListenerManager;
mActivityTaskManager = activityTaskManager;
mUiEventLogger = uiEventLogger;
mFalsingManager = falsingManager;
+ mInteractionJankMonitor = interactionJankMonitor;
mGuestResumeSessionReceiver = new GuestResumeSessionReceiver(
this, mUserTracker, mUiEventLogger, secureSettings);
mUserDetailAdapter = userDetailAdapter;
@@ -485,8 +496,11 @@
protected void switchToUserId(int id) {
try {
+ mInteractionJankMonitor.begin(InteractionJankMonitor.Configuration.Builder
+ .withView(InteractionJankMonitor.CUJ_USER_SWITCH, mRootView)
+ .setTimeout(MULTI_USER_JOURNEY_TIMEOUT));
pauseRefreshUsers();
- ActivityManager.getService().switchUser(id);
+ mActivityManager.switchUser(id);
} catch (RemoteException e) {
Log.e(TAG, "Couldn't switch user.", e);
}
@@ -793,6 +807,10 @@
return guest.id;
}
+ public void init(NotificationShadeWindowView notificationShadeWindowView) {
+ mRootView = notificationShadeWindowView;
+ }
+
public static abstract class BaseUserAdapter extends BaseAdapter {
final UserSwitcherController mController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 1eec639..9290879 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -23,7 +23,9 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.policy.AccessPointControllerImpl;
+import com.android.systemui.statusbar.connectivity.AccessPointControllerImpl;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
import com.android.systemui.statusbar.policy.CastController;
@@ -42,8 +44,6 @@
import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl;
import com.android.systemui.statusbar.policy.LocationController;
import com.android.systemui.statusbar.policy.LocationControllerImpl;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
import com.android.systemui.statusbar.policy.RotationLockController;
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
index 21d700e..f4558fe 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbConfirmActivity.java
@@ -16,180 +16,46 @@
package com.android.systemui.usb;
-import android.app.AlertDialog;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.PermissionChecker;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.hardware.usb.IUsbManager;
-import android.hardware.usb.UsbAccessory;
-import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbManager;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.TextView;
-
-import com.android.internal.app.AlertActivity;
-import com.android.internal.app.AlertController;
import com.android.systemui.R;
-public class UsbConfirmActivity extends AlertActivity
- implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
-
- private static final String TAG = "UsbConfirmActivity";
-
- private CheckBox mAlwaysUse;
- private TextView mClearDefaultHint;
- private UsbDevice mDevice;
- private UsbAccessory mAccessory;
- private ResolveInfo mResolveInfo;
- private boolean mPermissionGranted;
- private UsbDisconnectedReceiver mDisconnectedReceiver;
+/**
+ * Dialog shown to confirm the package to start when a USB device or accessory is attached and there
+ * is only one package that claims to handle this USB device or accessory.
+ */
+public class UsbConfirmActivity extends UsbDialogActivity {
@Override
- public void onCreate(Bundle icicle) {
- getWindow().addSystemFlags(
- WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-
- super.onCreate(icicle);
-
- Intent intent = getIntent();
- mDevice = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
- mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
- mResolveInfo = (ResolveInfo) intent.getParcelableExtra("rinfo");
- String packageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE);
-
- PackageManager packageManager = getPackageManager();
- String appName = mResolveInfo.loadLabel(packageManager).toString();
-
- final AlertController.AlertParams ap = mAlertParams;
- ap.mTitle = appName;
+ protected void onResume() {
+ super.onResume();
+ final int strId;
boolean useRecordWarning = false;
- if (mDevice == null) {
- ap.mMessage = getString(R.string.usb_accessory_confirm_prompt, appName,
- mAccessory.getDescription());
- mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
- } else {
- int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- boolean hasRecordPermission =
- PermissionChecker.checkPermissionForPreflight(
- this, android.Manifest.permission.RECORD_AUDIO, -1, uid,
- packageName)
- == android.content.pm.PackageManager.PERMISSION_GRANTED;
- boolean isAudioCaptureDevice = mDevice.getHasAudioCapture();
- useRecordWarning = isAudioCaptureDevice && !hasRecordPermission;
-
- int strID = useRecordWarning
+ if (mDialogHelper.isUsbDevice()) {
+ useRecordWarning = mDialogHelper.deviceHasAudioCapture()
+ && !mDialogHelper.packageHasAudioRecordingPermission();
+ strId = useRecordWarning
? R.string.usb_device_confirm_prompt_warn
: R.string.usb_device_confirm_prompt;
-
- ap.mMessage = getString(strID, appName, mDevice.getProductName());
- mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
+ } else {
+ // UsbAccessory case
+ strId = R.string.usb_accessory_confirm_prompt;
}
- ap.mPositiveButtonText = getString(android.R.string.ok);
- ap.mNegativeButtonText = getString(android.R.string.cancel);
- ap.mPositiveButtonListener = this;
- ap.mNegativeButtonListener = this;
-
- // add "always use" checkbox
+ setAlertParams(strId);
+ // Only show the "always use" checkbox if there is no USB/Record warning
if (!useRecordWarning) {
- LayoutInflater inflater = (LayoutInflater) getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
- mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
- if (mDevice == null) {
- mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
- mAccessory.getDescription()));
- } else {
- mAlwaysUse.setText(getString(R.string.always_use_device, appName,
- mDevice.getProductName()));
- }
- mAlwaysUse.setOnCheckedChangeListener(this);
- mClearDefaultHint = (TextView) ap.mView.findViewById(
- com.android.internal.R.id.clearDefaultHint);
- mClearDefaultHint.setVisibility(View.GONE);
+ addAlwaysUseCheckbox();
}
setupAlert();
-
}
@Override
- protected void onDestroy() {
- if (mDisconnectedReceiver != null) {
- unregisterReceiver(mDisconnectedReceiver);
- }
- super.onDestroy();
- }
-
- public void onClick(DialogInterface dialog, int which) {
- if (which == AlertDialog.BUTTON_POSITIVE) {
- try {
- IBinder b = ServiceManager.getService(USB_SERVICE);
- IUsbManager service = IUsbManager.Stub.asInterface(b);
- final int uid = mResolveInfo.activityInfo.applicationInfo.uid;
- final int userId = UserHandle.myUserId();
- boolean alwaysUse = mAlwaysUse != null ? mAlwaysUse.isChecked() : false;
- Intent intent = null;
-
- if (mDevice != null) {
- intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
- intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice);
-
- // grant permission for the device
- service.grantDevicePermission(mDevice, uid);
- // set or clear default setting
- if (alwaysUse) {
- service.setDevicePackage(
- mDevice, mResolveInfo.activityInfo.packageName, userId);
- } else {
- service.setDevicePackage(mDevice, null, userId);
- }
- } else if (mAccessory != null) {
- intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
- intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory);
-
- // grant permission for the accessory
- service.grantAccessoryPermission(mAccessory, uid);
- // set or clear default setting
- if (alwaysUse) {
- service.setAccessoryPackage(
- mAccessory, mResolveInfo.activityInfo.packageName, userId);
- } else {
- service.setAccessoryPackage(mAccessory, null, userId);
- }
- }
-
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setComponent(
- new ComponentName(mResolveInfo.activityInfo.packageName,
- mResolveInfo.activityInfo.name));
- startActivityAsUser(intent, new UserHandle(userId));
- } catch (Exception e) {
- Log.e(TAG, "Unable to start activity", e);
- }
- }
- finish();
- }
-
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (mClearDefaultHint == null) return;
-
- if(isChecked) {
- mClearDefaultHint.setVisibility(View.VISIBLE);
+ void onConfirm() {
+ mDialogHelper.grantUidAccessPermission();
+ if (isAlwaysUseChecked()) {
+ mDialogHelper.setDefaultPackage();
} else {
- mClearDefaultHint.setVisibility(View.GONE);
+ mDialogHelper.clearDefaultPackage();
}
+ mDialogHelper.confirmDialogStartActivity();
+ finish();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDialogActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogActivity.java
new file mode 100644
index 0000000..930bc33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogActivity.java
@@ -0,0 +1,127 @@
+/*
+ * 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.systemui.usb;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import com.android.systemui.R;
+
+abstract class UsbDialogActivity extends AlertActivity
+ implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
+
+ private static final String TAG = UsbDialogActivity.class.getSimpleName();
+
+ UsbDialogHelper mDialogHelper;
+ private CheckBox mAlwaysUse;
+ private TextView mClearDefaultHint;
+
+ @Override
+ protected final void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ try {
+ mDialogHelper = new UsbDialogHelper(getApplicationContext(), getIntent());
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "unable to initialize", e);
+ finish();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mDialogHelper.registerUsbDisconnectedReceiver(this);
+ }
+
+ @Override
+ protected void onPause() {
+ if (mDialogHelper != null) {
+ mDialogHelper.unregisterUsbDisconnectedReceiver(this);
+ }
+ super.onPause();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == AlertDialog.BUTTON_POSITIVE) {
+ onConfirm();
+ } else {
+ finish();
+ }
+ }
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (mClearDefaultHint == null) return;
+
+ if (isChecked) {
+ mClearDefaultHint.setVisibility(View.VISIBLE);
+ } else {
+ mClearDefaultHint.setVisibility(View.GONE);
+ }
+ }
+
+ void setAlertParams(int strId) {
+ final AlertController.AlertParams ap = mAlertParams;
+ ap.mTitle = mDialogHelper.getAppName();
+ ap.mMessage = getString(strId, mDialogHelper.getAppName(),
+ mDialogHelper.getDeviceDescription());
+ ap.mPositiveButtonText = getString(android.R.string.ok);
+ ap.mNegativeButtonText = getString(android.R.string.cancel);
+ ap.mPositiveButtonListener = this;
+ ap.mNegativeButtonListener = this;
+ }
+
+ void addAlwaysUseCheckbox() {
+ final AlertController.AlertParams ap = mAlertParams;
+ LayoutInflater inflater = getSystemService(LayoutInflater.class);
+ ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+ mAlwaysUse = ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
+ if (mDialogHelper.isUsbAccessory()) {
+ mAlwaysUse.setText(getString(R.string.always_use_accessory, mDialogHelper.getAppName(),
+ mDialogHelper.getDeviceDescription()));
+ } else {
+ // UsbDevice case
+ mAlwaysUse.setText(getString(R.string.always_use_device, mDialogHelper.getAppName(),
+ mDialogHelper.getDeviceDescription()));
+ }
+ mAlwaysUse.setOnCheckedChangeListener(this);
+ mClearDefaultHint = ap.mView.findViewById(com.android.internal.R.id.clearDefaultHint);
+ mClearDefaultHint.setVisibility(View.GONE);
+ }
+
+ boolean isAlwaysUseChecked() {
+ return mAlwaysUse != null && mAlwaysUse.isChecked();
+ }
+
+ /**
+ * Called when the dialog is confirmed.
+ */
+ abstract void onConfirm();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java
new file mode 100644
index 0000000..af804aa
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDialogHelper.java
@@ -0,0 +1,299 @@
+/*
+ * 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.systemui.usb;
+
+import static android.Manifest.permission.RECORD_AUDIO;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.PermissionChecker;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.hardware.usb.IUsbManager;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.systemui.usb.tv.TvUsbPermissionActivity;
+
+
+/**
+ * Helper class to separate model and view for USB permission and confirm dialogs.
+ */
+public class UsbDialogHelper {
+ private static final String TAG = TvUsbPermissionActivity.class.getSimpleName();
+ private static final String EXTRA_RESOLVE_INFO = "rinfo";
+
+ private final UsbDevice mDevice;
+ private final UsbAccessory mAccessory;
+ private final ResolveInfo mResolveInfo;
+ private final String mPackageName;
+ private final CharSequence mAppName;
+ private final Context mContext;
+ private final PendingIntent mPendingIntent;
+ private final IUsbManager mUsbService;
+ private final int mUid;
+ private final boolean mCanBeDefault;
+
+ private UsbDisconnectedReceiver mDisconnectedReceiver;
+ private boolean mIsUsbDevice;
+ private boolean mResponseSent;
+
+ /**
+ * @param context The Context of the caller.
+ * @param intent The intent of the caller.
+ * @throws IllegalStateException Thrown if both UsbDevice and UsbAccessory are null or if the
+ * query for the matching ApplicationInfo is unsuccessful.
+ */
+ public UsbDialogHelper(Context context, Intent intent) throws IllegalStateException {
+ mContext = context;
+ mDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ mAccessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
+ mCanBeDefault = intent.getBooleanExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, false);
+ if (mDevice == null && mAccessory == null) {
+ throw new IllegalStateException("Device and accessory are both null.");
+ }
+ if (mDevice != null) {
+ mIsUsbDevice = true;
+ }
+ mResolveInfo = intent.getParcelableExtra(EXTRA_RESOLVE_INFO);
+ PackageManager packageManager = mContext.getPackageManager();
+ if (mResolveInfo != null) {
+ // If a ResolveInfo is provided it will be used to determine the activity to start
+ mUid = mResolveInfo.activityInfo.applicationInfo.uid;
+ mPackageName = mResolveInfo.activityInfo.packageName;
+ mPendingIntent = null;
+ } else {
+ mUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ mPackageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE);
+ mPendingIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+ }
+ try {
+ ApplicationInfo aInfo = packageManager.getApplicationInfo(mPackageName, 0);
+ mAppName = aInfo.loadLabel(packageManager);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalStateException("unable to look up package name", e);
+ }
+ IBinder b = ServiceManager.getService(Context.USB_SERVICE);
+ mUsbService = IUsbManager.Stub.asInterface(b);
+ }
+
+ /**
+ * Registers UsbDisconnectedReceiver to dismiss dialog automatically when device or accessory
+ * gets disconnected
+ * @param activity The activity to finish when device / accessory gets disconnected.
+ */
+ public void registerUsbDisconnectedReceiver(Activity activity) {
+ if (mIsUsbDevice) {
+ mDisconnectedReceiver = new UsbDisconnectedReceiver(activity, mDevice);
+ } else {
+ mDisconnectedReceiver = new UsbDisconnectedReceiver(activity, mAccessory);
+ }
+ }
+
+ /**
+ * Unregisters the UsbDisconnectedReceiver. To be called when the activity is destroyed.
+ * @param activity The activity registered to finish when device / accessory gets disconnected.
+ */
+ public void unregisterUsbDisconnectedReceiver(Activity activity) {
+ if (mDisconnectedReceiver != null) {
+ try {
+ activity.unregisterReceiver(mDisconnectedReceiver);
+ } catch (Exception e) {
+ // pass
+ }
+ mDisconnectedReceiver = null;
+ }
+ }
+
+ /**
+ * @return True if the intent contains a UsbDevice which can capture audio.
+ */
+ public boolean deviceHasAudioCapture() {
+ return mDevice != null && mDevice.getHasAudioCapture();
+ }
+
+ /**
+ * @return True if the package has RECORD_AUDIO permission specified in its manifest.
+ */
+ public boolean packageHasAudioRecordingPermission() {
+ return PermissionChecker.checkPermissionForPreflight(mContext, RECORD_AUDIO,
+ PermissionChecker.PID_UNKNOWN, mUid, mPackageName)
+ == android.content.pm.PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * @return True if the intent contains a UsbDevice.
+ */
+ public boolean isUsbDevice() {
+ return mIsUsbDevice;
+ }
+
+ /**
+ * @return True if the intent contains a UsbAccessory.
+ */
+ public boolean isUsbAccessory() {
+ return !mIsUsbDevice;
+ }
+
+ /**
+ * Grants USB permission to the device / accessory to the calling uid.
+ */
+ public void grantUidAccessPermission() {
+ try {
+ if (mIsUsbDevice) {
+ mUsbService.grantDevicePermission(mDevice, mUid);
+ } else {
+ mUsbService.grantAccessoryPermission(mAccessory, mUid);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "IUsbService connection failed", e);
+ }
+ }
+
+ /**
+ * Sets the package as default for the device / accessory.
+ */
+ public void setDefaultPackage() {
+ final int userId = UserHandle.myUserId();
+ try {
+ if (mIsUsbDevice) {
+ mUsbService.setDevicePackage(mDevice, mPackageName, userId);
+ } else {
+ mUsbService.setAccessoryPackage(mAccessory, mPackageName, userId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "IUsbService connection failed", e);
+ }
+ }
+
+ /**
+ * Clears the default package of the device / accessory.
+ */
+ public void clearDefaultPackage() {
+ final int userId = UserHandle.myUserId();
+ try {
+ if (mIsUsbDevice) {
+ mUsbService.setDevicePackage(mDevice, null, userId);
+ } else {
+ mUsbService.setAccessoryPackage(mAccessory, null, userId);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "IUsbService connection failed", e);
+ }
+ }
+
+ /**
+ * Starts the activity which was selected to handle the device / accessory.
+ */
+ public void confirmDialogStartActivity() {
+ final int userId = UserHandle.myUserId();
+ Intent intent;
+
+ if (mIsUsbDevice) {
+ intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice);
+ } else {
+ intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
+ intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory);
+ }
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setComponent(
+ new ComponentName(mResolveInfo.activityInfo.packageName,
+ mResolveInfo.activityInfo.name));
+ try {
+ mContext.startActivityAsUser(intent, new UserHandle(userId));
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to start activity", e);
+ }
+ }
+
+ /**
+ * Sends the result of the permission dialog via the provided PendingIntent.
+ *
+ * @param permissionGranted True if the user pressed ok in the permission dialog.
+ */
+ public void sendPermissionDialogResponse(boolean permissionGranted) {
+ if (!mResponseSent) {
+ // send response via pending intent
+ Intent intent = new Intent();
+ if (mIsUsbDevice) {
+ intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice);
+ } else {
+ intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory);
+ }
+ intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, permissionGranted);
+ try {
+ mPendingIntent.send(mContext, 0, intent);
+ mResponseSent = true;
+ } catch (PendingIntent.CanceledException e) {
+ Log.w(TAG, "PendingIntent was cancelled");
+ }
+ }
+ }
+
+ /**
+ * @return A description of the device / accessory
+ */
+ public String getDeviceDescription() {
+ String desc;
+ if (mIsUsbDevice) {
+ desc = mDevice.getProductName();
+ if (desc == null) {
+ desc = mDevice.getDeviceName();
+ }
+ } else {
+ // UsbAccessory
+ desc = mAccessory.getDescription();
+ if (desc == null) {
+ desc = String.format("%s %s", mAccessory.getManufacturer(), mAccessory.getModel());
+ }
+ }
+ return desc;
+ }
+
+ /**
+ * Whether the calling package can set as default handler of the USB device or accessory.
+ * In case of a UsbAccessory this is the case if the calling package has an intent filter for
+ * {@link UsbManager#ACTION_USB_ACCESSORY_ATTACHED} with a usb-accessory filter matching the
+ * attached accessory. In case of a UsbDevice this is the case if the calling package has an
+ * intent filter for {@link UsbManager#ACTION_USB_DEVICE_ATTACHED} with a usb-device filter
+ * matching the attached device.
+ *
+ * @return True if the package can be default for the USB device.
+ */
+ public boolean canBeDefault() {
+ return mCanBeDefault;
+ }
+
+ /**
+ * @return The name of the app which requested permission or the name of the app which will be
+ * opened if the user allows it to handle the USB device.
+ */
+ public CharSequence getAppName() {
+ return mAppName;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
index bfa50bc..38d6347 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbPermissionActivity.java
@@ -16,186 +16,53 @@
package com.android.systemui.usb;
-import android.app.AlertDialog;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.PermissionChecker;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.hardware.usb.IUsbManager;
-import android.hardware.usb.UsbAccessory;
-import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbManager;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.TextView;
-
-import com.android.internal.app.AlertActivity;
-import com.android.internal.app.AlertController;
import com.android.systemui.R;
-public class UsbPermissionActivity extends AlertActivity
- implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener {
+/**
+ * Dialog shown when a package requests access to a USB device or accessory.
+ */
+public class UsbPermissionActivity extends UsbDialogActivity {
- private static final String TAG = "UsbPermissionActivity";
-
- private CheckBox mAlwaysUse;
- private TextView mClearDefaultHint;
- private UsbDevice mDevice;
- private UsbAccessory mAccessory;
- private PendingIntent mPendingIntent;
- private String mPackageName;
- private int mUid;
- private boolean mPermissionGranted;
- private UsbDisconnectedReceiver mDisconnectedReceiver;
+ private boolean mPermissionGranted = false;
@Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- getWindow().addPrivateFlags(
- WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
- Intent intent = getIntent();
- mDevice = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
- mAccessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
- mPendingIntent = (PendingIntent)intent.getParcelableExtra(Intent.EXTRA_INTENT);
- mUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- mPackageName = intent.getStringExtra(UsbManager.EXTRA_PACKAGE);
- boolean canBeDefault = intent.getBooleanExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, false);
-
- PackageManager packageManager = getPackageManager();
- ApplicationInfo aInfo;
- try {
- aInfo = packageManager.getApplicationInfo(mPackageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "unable to look up package name", e);
- finish();
- return;
- }
- String appName = aInfo.loadLabel(packageManager).toString();
-
- final AlertController.AlertParams ap = mAlertParams;
- ap.mTitle = appName;
+ protected void onResume() {
+ super.onResume();
+ final int strId;
boolean useRecordWarning = false;
- if (mDevice == null) {
- // Accessory Case
-
- ap.mMessage = getString(R.string.usb_accessory_permission_prompt, appName,
- mAccessory.getDescription());
- mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mAccessory);
- } else {
- boolean hasRecordPermission =
- PermissionChecker.checkPermissionForPreflight(
- this, android.Manifest.permission.RECORD_AUDIO, -1, aInfo.uid,
- mPackageName)
- == android.content.pm.PackageManager.PERMISSION_GRANTED;
- boolean isAudioCaptureDevice = mDevice.getHasAudioCapture();
- useRecordWarning = isAudioCaptureDevice && !hasRecordPermission;
-
- int strID = useRecordWarning
+ if (mDialogHelper.isUsbDevice()) {
+ useRecordWarning = mDialogHelper.deviceHasAudioCapture()
+ && !mDialogHelper.packageHasAudioRecordingPermission();
+ strId = useRecordWarning
? R.string.usb_device_permission_prompt_warn
: R.string.usb_device_permission_prompt;
- ap.mMessage = getString(strID, appName, mDevice.getProductName());
- mDisconnectedReceiver = new UsbDisconnectedReceiver(this, mDevice);
-
+ } else {
+ // UsbAccessory case
+ strId = R.string.usb_accessory_permission_prompt;
}
-
- ap.mPositiveButtonText = getString(android.R.string.ok);
- ap.mNegativeButtonText = getString(android.R.string.cancel);
- ap.mPositiveButtonListener = this;
- ap.mNegativeButtonListener = this;
-
- // Don't show the "always use" checkbox if the USB/Record warning is in effect
- if (!useRecordWarning && canBeDefault && (mDevice != null || mAccessory != null)) {
- // add "open when" checkbox
- LayoutInflater inflater = (LayoutInflater) getSystemService(
- Context.LAYOUT_INFLATER_SERVICE);
- ap.mView = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
- mAlwaysUse = (CheckBox) ap.mView.findViewById(com.android.internal.R.id.alwaysUse);
- if (mDevice == null) {
- mAlwaysUse.setText(getString(R.string.always_use_accessory, appName,
- mAccessory.getDescription()));
- } else {
- mAlwaysUse.setText(getString(R.string.always_use_device, appName,
- mDevice.getProductName()));
- }
- mAlwaysUse.setOnCheckedChangeListener(this);
-
- mClearDefaultHint = (TextView)ap.mView.findViewById(
- com.android.internal.R.id.clearDefaultHint);
- mClearDefaultHint.setVisibility(View.GONE);
+ setAlertParams(strId);
+ // Only show the "always use" checkbox if there is no USB/Record warning
+ if (!useRecordWarning && mDialogHelper.canBeDefault()) {
+ addAlwaysUseCheckbox();
}
-
setupAlert();
}
@Override
- public void onDestroy() {
- IBinder b = ServiceManager.getService(USB_SERVICE);
- IUsbManager service = IUsbManager.Stub.asInterface(b);
-
- // send response via pending intent
- Intent intent = new Intent();
- try {
- if (mDevice != null) {
- intent.putExtra(UsbManager.EXTRA_DEVICE, mDevice);
- if (mPermissionGranted) {
- service.grantDevicePermission(mDevice, mUid);
- if (mAlwaysUse != null && mAlwaysUse.isChecked()) {
- final int userId = UserHandle.getUserId(mUid);
- service.setDevicePackage(mDevice, mPackageName, userId);
- }
- }
- }
- if (mAccessory != null) {
- intent.putExtra(UsbManager.EXTRA_ACCESSORY, mAccessory);
- if (mPermissionGranted) {
- service.grantAccessoryPermission(mAccessory, mUid);
- if (mAlwaysUse != null && mAlwaysUse.isChecked()) {
- final int userId = UserHandle.getUserId(mUid);
- service.setAccessoryPackage(mAccessory, mPackageName, userId);
- }
- }
- }
- intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, mPermissionGranted);
- mPendingIntent.send(this, 0, intent);
- } catch (PendingIntent.CanceledException e) {
- Log.w(TAG, "PendingIntent was cancelled");
- } catch (RemoteException e) {
- Log.e(TAG, "IUsbService connection failed", e);
+ protected void onPause() {
+ if (isFinishing()) {
+ mDialogHelper.sendPermissionDialogResponse(mPermissionGranted);
}
-
- if (mDisconnectedReceiver != null) {
- unregisterReceiver(mDisconnectedReceiver);
- }
- super.onDestroy();
+ super.onPause();
}
- public void onClick(DialogInterface dialog, int which) {
- if (which == AlertDialog.BUTTON_POSITIVE) {
- mPermissionGranted = true;
+ @Override
+ void onConfirm() {
+ mDialogHelper.grantUidAccessPermission();
+ if (isAlwaysUseChecked()) {
+ mDialogHelper.setDefaultPackage();
}
+ mPermissionGranted = true;
finish();
}
-
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (mClearDefaultHint == null) return;
-
- if(isChecked) {
- mClearDefaultHint.setVisibility(View.VISIBLE);
- } else {
- mClearDefaultHint.setVisibility(View.GONE);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
index 0aa965b..6c35433 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbResolverActivity.java
@@ -19,6 +19,7 @@
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.hardware.usb.IUsbManager;
@@ -62,6 +63,8 @@
Intent intent = getIntent();
Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
if (!(targetParcelable instanceof Intent)) {
+ super.onCreate(savedInstanceState);
+ onStop(); // unregister receiver registered in onCreate (PackageMonitor)
Log.w("UsbResolverActivity", "Target is not an intent: " + targetParcelable);
finish();
return;
@@ -94,6 +97,8 @@
} else {
mAccessory = (UsbAccessory)target.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (mAccessory == null) {
+ super.onCreate(savedInstanceState);
+ onStop(); // unregister receiver registered in onCreate (PackageMonitor)
Log.e(TAG, "no device or accessory");
finish();
return;
@@ -109,7 +114,9 @@
mOtherProfileIntent.putParcelableArrayListExtra(EXTRA_RESOLVE_INFOS,
rListOtherProfile);
} else {
- mOtherProfileIntent = new Intent(this, UsbConfirmActivity.class);
+ mOtherProfileIntent.setComponent(ComponentName.unflattenFromString(
+ this.getResources().getString(
+ com.android.internal.R.string.config_usbConfirmActivity)));
mOtherProfileIntent.putExtra(EXTRA_RESOLVE_INFO, rListOtherProfile.get(0));
if (mDevice != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbConfirmActivity.java
new file mode 100644
index 0000000..b03bedc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbConfirmActivity.java
@@ -0,0 +1,53 @@
+/*
+ * 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.systemui.usb.tv;
+
+import com.android.systemui.R;
+
+/**
+ * Dialog shown to confirm the package to start when a USB device or accessory is attached and there
+ * is only one package that claims to handle this USB device or accessory.
+ */
+public class TvUsbConfirmActivity extends TvUsbDialogActivity {
+ private static final String TAG = TvUsbConfirmActivity.class.getSimpleName();
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ final int strId;
+ if (mDialogHelper.isUsbDevice()) {
+ boolean useRecordWarning = mDialogHelper.deviceHasAudioCapture()
+ && !mDialogHelper.packageHasAudioRecordingPermission();
+ strId = useRecordWarning
+ ? R.string.usb_device_confirm_prompt_warn
+ : R.string.usb_device_confirm_prompt;
+ } else {
+ // UsbAccessory case
+ strId = R.string.usb_accessory_confirm_prompt;
+ }
+ CharSequence text = getString(strId, mDialogHelper.getAppName(),
+ mDialogHelper.getDeviceDescription());
+ initUI(mDialogHelper.getAppName(), text);
+ }
+
+ @Override
+ void onConfirm() {
+ mDialogHelper.grantUidAccessPermission();
+ mDialogHelper.confirmDialogStartActivity();
+ finish();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbDialogActivity.java b/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbDialogActivity.java
new file mode 100644
index 0000000..1c003ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbDialogActivity.java
@@ -0,0 +1,95 @@
+/*
+ * 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.systemui.usb.tv;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.tv.TvBottomSheetActivity;
+import com.android.systemui.usb.UsbDialogHelper;
+
+abstract class TvUsbDialogActivity extends TvBottomSheetActivity implements View.OnClickListener {
+ private static final String TAG = TvUsbDialogActivity.class.getSimpleName();
+ UsbDialogHelper mDialogHelper;
+
+ @Override
+ public final void onCreate(Bundle b) {
+ super.onCreate(b);
+ getWindow().addPrivateFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ try {
+ mDialogHelper = new UsbDialogHelper(getApplicationContext(), getIntent());
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "unable to initialize", e);
+ finish();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mDialogHelper.registerUsbDisconnectedReceiver(this);
+ }
+
+ @Override
+ protected void onPause() {
+ if (mDialogHelper != null) {
+ mDialogHelper.unregisterUsbDisconnectedReceiver(this);
+ }
+ super.onPause();
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.bottom_sheet_positive_button) {
+ onConfirm();
+ } else {
+ finish();
+ }
+ }
+
+ /**
+ * Called when the ok button is clicked.
+ */
+ abstract void onConfirm();
+
+ void initUI(CharSequence title, CharSequence text) {
+ TextView titleTextView = findViewById(R.id.bottom_sheet_title);
+ TextView contentTextView = findViewById(R.id.bottom_sheet_body);
+ ImageView icon = findViewById(R.id.bottom_sheet_icon);
+ ImageView secondIcon = findViewById(R.id.bottom_sheet_second_icon);
+ Button okButton = findViewById(R.id.bottom_sheet_positive_button);
+ Button cancelButton = findViewById(R.id.bottom_sheet_negative_button);
+
+ titleTextView.setText(title);
+ contentTextView.setText(text);
+ icon.setImageResource(com.android.internal.R.drawable.ic_usb_48dp);
+ secondIcon.setVisibility(View.GONE);
+ okButton.setText(android.R.string.ok);
+ okButton.setOnClickListener(this);
+
+ cancelButton.setText(android.R.string.cancel);
+ cancelButton.setOnClickListener(this);
+ cancelButton.requestFocus();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbPermissionActivity.java
new file mode 100644
index 0000000..7b415b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbPermissionActivity.java
@@ -0,0 +1,62 @@
+/*
+ * 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.systemui.usb.tv;
+
+import com.android.systemui.R;
+
+/**
+ * Dialog shown when a package requests access to a USB device or accessory on TVs.
+ */
+public class TvUsbPermissionActivity extends TvUsbDialogActivity {
+ private static final String TAG = TvUsbPermissionActivity.class.getSimpleName();
+
+ private boolean mPermissionGranted = false;
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ final int strId;
+ if (mDialogHelper.isUsbDevice()) {
+ boolean useRecordWarning = mDialogHelper.deviceHasAudioCapture()
+ && !mDialogHelper.packageHasAudioRecordingPermission();
+ strId = useRecordWarning
+ ? R.string.usb_device_permission_prompt_warn
+ : R.string.usb_device_permission_prompt;
+ } else {
+ // UsbAccessory case
+ strId = R.string.usb_accessory_permission_prompt;
+ }
+ CharSequence text = getString(strId, mDialogHelper.getAppName(),
+ mDialogHelper.getDeviceDescription());
+ initUI(mDialogHelper.getAppName(), text);
+ }
+
+ @Override
+ protected void onPause() {
+ if (isFinishing()) {
+ mDialogHelper.sendPermissionDialogResponse(mPermissionGranted);
+ }
+ super.onPause();
+ }
+
+ @Override
+ void onConfirm() {
+ mDialogHelper.grantUidAccessPermission();
+ mPermissionGranted = true;
+ finish();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
index a981045..2c01a70 100644
--- a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
@@ -40,8 +40,8 @@
this.wallpaperInfo = wallpaperInfo
}
- private val shouldUseDefaultDeviceStateChangeTransition: Boolean
- get() = wallpaperInfo?.shouldUseDefaultDeviceStateChangeTransition()
+ private val shouldUseDefaultDisplayStateChangeTransition: Boolean
+ get() = wallpaperInfo?.shouldUseDefaultDisplayStateChangeTransition()
?: true
fun setNotificationShadeZoom(zoomOut: Float) {
@@ -50,7 +50,7 @@
}
fun setUnfoldTransitionZoom(zoomOut: Float) {
- if (shouldUseDefaultDeviceStateChangeTransition) {
+ if (shouldUseDefaultDisplayStateChangeTransition) {
unfoldTransitionZoomOut = zoomOut
updateZoom()
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index ce02b83..e4336fe 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -97,6 +97,7 @@
mLargeClockFrame = mKeyguardClockSwitch.findViewById(R.id.lockscreen_clock_view_large);
mLargeClockView = mKeyguardClockSwitch.findViewById(R.id.animatable_clock_view_large);
mBigClock = new TextClock(getContext());
+ mKeyguardClockSwitch.mChildrenAreLaidOut = true;
MockitoAnnotations.initMocks(this);
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index d3557d4..0772b20 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -78,6 +78,7 @@
import androidx.lifecycle.Observer;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockPatternUtils;
@@ -171,6 +172,8 @@
@Mock
private FeatureFlags mFeatureFlags;
@Mock
+ private InteractionJankMonitor mInteractionJankMonitor;
+ @Mock
private Vibrator mVibrator;
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor;
@@ -737,6 +740,16 @@
}
@Test
+ public void testMultiUserJankMonitor_whenUserSwitches() throws Exception {
+ final IRemoteCallback reply = new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) {} // do nothing
+ };
+ mKeyguardUpdateMonitor.handleUserSwitchComplete(10 /* user */);
+ verify(mInteractionJankMonitor).end(eq(InteractionJankMonitor.CUJ_USER_SWITCH));
+ }
+
+ @Test
public void testGetUserCanSkipBouncer_whenTrust() {
int user = KeyguardUpdateMonitor.getCurrentUser();
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */);
@@ -1051,7 +1064,7 @@
mRingerModeTracker, mBackgroundExecutor,
mStatusBarStateController, mLockPatternUtils,
mAuthController, mTelephonyListenerManager, mFeatureFlags,
- mVibrator);
+ mInteractionJankMonitor, mVibrator);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 4c7f959e..c6df1c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -28,11 +28,13 @@
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -151,27 +153,155 @@
reset(mTunerService);
}
+
+ private void verifyRoundedCornerViewsVisibility(
+ @DisplayCutout.BoundsPosition final int overlayId,
+ @View.Visibility final int visibility) {
+ final View overlay = mScreenDecorations.mOverlays[overlayId];
+ final View left = overlay.findViewById(R.id.left);
+ final View right = overlay.findViewById(R.id.right);
+ assertNotNull(left);
+ assertNotNull(right);
+ assertThat(left.getVisibility()).isEqualTo(visibility);
+ assertThat(right.getVisibility()).isEqualTo(visibility);
+ }
+
+ private void verifyTopDotViewsNullable(final boolean isAssertNull) {
+ if (isAssertNull) {
+ assertNull(mScreenDecorations.mTopLeftDot);
+ assertNull(mScreenDecorations.mTopRightDot);
+ } else {
+ assertNotNull(mScreenDecorations.mTopLeftDot);
+ assertNotNull(mScreenDecorations.mTopRightDot);
+ }
+ }
+
+ private void verifyBottomDotViewsNullable(final boolean isAssertNull) {
+ if (isAssertNull) {
+ assertNull(mScreenDecorations.mBottomLeftDot);
+ assertNull(mScreenDecorations.mBottomRightDot);
+ } else {
+ assertNotNull(mScreenDecorations.mBottomLeftDot);
+ assertNotNull(mScreenDecorations.mBottomRightDot);
+ }
+ }
+
+ private void verifyDotViewsNullable(final boolean isAssertNull) {
+ verifyTopDotViewsNullable(isAssertNull);
+ verifyBottomDotViewsNullable(isAssertNull);
+ }
+
+ private void verifyTopDotViewsVisibility(@View.Visibility final int visibility) {
+ verifyTopDotViewsNullable(false);
+ assertThat(mScreenDecorations.mTopLeftDot.getVisibility()).isEqualTo(visibility);
+ assertThat(mScreenDecorations.mTopRightDot.getVisibility()).isEqualTo(visibility);
+ }
+
+ private void verifyBottomDotViewsVisibility(@View.Visibility final int visibility) {
+ verifyBottomDotViewsNullable(false);
+ assertThat(mScreenDecorations.mBottomLeftDot.getVisibility()).isEqualTo(visibility);
+ assertThat(mScreenDecorations.mBottomRightDot.getVisibility()).isEqualTo(visibility);
+ }
+
+ private void verifyDotViewsVisibility(@View.Visibility final int visibility) {
+ verifyTopDotViewsVisibility(visibility);
+ verifyBottomDotViewsVisibility(visibility);
+ }
+
+ private void verifyOverlaysExistAndAdded(final boolean left, final boolean top,
+ final boolean right, final boolean bottom) {
+ if (left || top || right || bottom) {
+ assertNotNull(mScreenDecorations.mOverlays);
+ } else {
+ verify(mWindowManager, never()).addView(any(), any());
+ return;
+ }
+
+ if (left) {
+ assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
+ verify(mWindowManager, times(1))
+ .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]), any());
+ } else {
+ assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
+ }
+
+ if (top) {
+ assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
+ verify(mWindowManager, times(1))
+ .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
+ } else {
+ assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
+ }
+
+ if (right) {
+ assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
+ verify(mWindowManager, times(1))
+ .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]), any());
+ } else {
+ assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
+ }
+
+ if (bottom) {
+ assertNotNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
+ verify(mWindowManager, times(1))
+ .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
+ } else {
+ assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
+ }
+ }
+
@Test
- public void testNoRounding_NoCutout() {
+ public void testNoRounding_NoCutout_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
- false /* fillCutout */);
+ false /* fillCutout */, false /* privacyDot */);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
mScreenDecorations.start();
// No views added.
- verify(mWindowManager, never()).addView(any(), any());
+ verifyOverlaysExistAndAdded(false, false, false, false);
// No Tuners tuned.
verify(mTunerService, never()).addTunable(any(), any());
+ // No dot controller init
+ verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
- public void testRounding_NoCutout() {
+ public void testNoRounding_NoCutout_PrivacyDot() {
+ setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
+ 0 /* roundedPadding */, false /* multipleRadius */,
+ false /* fillCutout */, true /* privacyDot */);
+
+ // no cutout
+ doReturn(null).when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+
+ // Top and bottom windows are created for privacy dot.
+ // Left and right window should be null.
+ verifyOverlaysExistAndAdded(false, true, false, true);
+
+ // Rounded corner views shall not exist
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
+
+ // Privacy dots shall exist but invisible
+ verifyDotViewsVisibility(View.INVISIBLE);
+
+ // One tunable.
+ verify(mTunerService, times(1)).addTunable(any(), any());
+ // Dot controller init
+ verify(mDotViewController, times(1)).initialize(
+ isA(View.class), isA(View.class), isA(View.class), isA(View.class));
+ }
+
+ @Test
+ public void testRounding_NoCutout_NoPrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
20 /* roundedPadding */, false /* multipleRadius */,
- false /* fillCutout */);
+ false /* fillCutout */, false /* privacyDot */);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
@@ -179,17 +309,49 @@
mScreenDecorations.start();
// Top and bottom windows are created for rounded corners.
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
-
// Left and right window should be null.
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
+ verifyOverlaysExistAndAdded(false, true, false, true);
+
+ // Rounded corner views shall exist
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+
+ // Privacy dots shall not exist
+ verifyDotViewsNullable(true);
// One tunable.
verify(mTunerService, times(1)).addTunable(any(), any());
+ // No dot controller init
+ verify(mDotViewController, never()).initialize(any(), any(), any(), any());
+ }
+
+ @Test
+ public void testRounding_NoCutout_PrivacyDot() {
+ setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
+ 20 /* roundedPadding */, false /* multipleRadius */,
+ false /* fillCutout */, true /* privacyDot */);
+
+ // no cutout
+ doReturn(null).when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+
+ // Top and bottom windows are created for rounded corners.
+ // Left and right window should be null.
+ verifyOverlaysExistAndAdded(false, true, false, true);
+
+ // Rounded corner views shall exist
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+
+ // Privacy dots shall exist but invisible
+ verifyDotViewsVisibility(View.INVISIBLE);
+
+ // One tunable.
+ verify(mTunerService, times(1)).addTunable(any(), any());
+ // Dot controller init
+ verify(mDotViewController, times(1)).initialize(
+ isA(View.class), isA(View.class), isA(View.class), isA(View.class));
}
@Test
@@ -197,7 +359,7 @@
final Point testRadiusPoint = new Point(1, 1);
setupResources(1 /* radius */, 1 /* radiusTop */, 1 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
- false /* fillCutout */);
+ false /* fillCutout */, true /* privacyDot */);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
@@ -213,7 +375,7 @@
final int testTopRadius = 1;
final int testBottomRadius = 5;
setupResources(testTopRadius, testTopRadius, testBottomRadius, 0 /* roundedPadding */,
- false /* multipleRadius */, false /* fillCutout */);
+ false /* multipleRadius */, false /* fillCutout */, true /* privacyDot */);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
@@ -242,7 +404,7 @@
final int testTopRadius = 1;
final int testBottomRadius = 5;
setupResources(testTopRadius, testTopRadius, testBottomRadius, 0 /* roundedPadding */,
- false /* multipleRadius */, false /* fillCutout */);
+ false /* multipleRadius */, false /* fillCutout */, true /* privacyDot */);
// left cutout
final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
@@ -278,29 +440,32 @@
}
@Test
- public void testRoundingMultipleRadius_NoCutout() {
+ public void testRoundingMultipleRadius_NoCutout_NoPrivacyDot() {
final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded);
final Point multipleRadiusSize = new Point(d.getIntrinsicWidth(), d.getIntrinsicHeight());
setupResources(9999 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
9999 /* roundedPadding */, true /* multipleRadius */,
- false /* fillCutout */);
+ false /* fillCutout */, false /* privacyDot */);
// no cutout
doReturn(null).when(mScreenDecorations).getCutout();
mScreenDecorations.start();
// Top and bottom windows are created for rounded corners.
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
-
// Left and right window should be null.
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
+ verifyOverlaysExistAndAdded(false, true, false, true);
+
+ // Rounded corner views shall exist
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+
+ // Privacy dots shall not exist
+ verifyDotViewsNullable(true);
// One tunable.
verify(mTunerService, times(1)).addTunable(any(), any());
+ // No dot controller init
+ verify(mDotViewController, never()).initialize(any(), any(), any(), any());
// Size of corner view should exactly match max(width, height) of R.drawable.rounded
assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(multipleRadiusSize);
@@ -309,10 +474,45 @@
}
@Test
- public void testNoRounding_CutoutShortEdge() {
+ public void testRoundingMultipleRadius_NoCutout_PrivacyDot() {
+ final VectorDrawable d = (VectorDrawable) mContext.getDrawable(R.drawable.rounded);
+ final Point multipleRadiusSize = new Point(d.getIntrinsicWidth(), d.getIntrinsicHeight());
+ setupResources(9999 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
+ 9999 /* roundedPadding */, true /* multipleRadius */,
+ false /* fillCutout */, true /* privacyDot */);
+
+ // no cutout
+ doReturn(null).when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+ // Top and bottom windows are created for rounded corners.
+ // Left and right window should be null.
+ verifyOverlaysExistAndAdded(false, true, false, true);
+
+ // Rounded corner views shall exist
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+
+ // Privacy dots shall exist but invisible
+ verifyDotViewsVisibility(View.INVISIBLE);
+
+ // One tunable.
+ verify(mTunerService, times(1)).addTunable(any(), any());
+ // Dot controller init
+ verify(mDotViewController, times(1)).initialize(
+ isA(View.class), isA(View.class), isA(View.class), isA(View.class));
+
+ // Size of corner view should exactly match max(width, height) of R.drawable.rounded
+ assertThat(mScreenDecorations.mRoundedDefault).isEqualTo(multipleRadiusSize);
+ assertThat(mScreenDecorations.mRoundedDefaultTop).isEqualTo(multipleRadiusSize);
+ assertThat(mScreenDecorations.mRoundedDefaultBottom).isEqualTo(multipleRadiusSize);
+ }
+
+ @Test
+ public void testNoRounding_CutoutShortEdge_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
- true /* fillCutout */);
+ true /* fillCutout */, false /* privacyDot */);
// top cutout
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
@@ -321,21 +521,53 @@
mScreenDecorations.start();
// Top window is created for top cutout.
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
- // Bottom window should be null.
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
- // Left window should be null.
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
- // Right window should be null.
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
+ // Bottom, left, or right window should be null.
+ verifyOverlaysExistAndAdded(false, true, false, false);
+
+ // Privacy dots shall not exist because of no privacy
+ verifyDotViewsNullable(true);
+
+ // No dot controller init
+ verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
- public void testNoRounding_CutoutLongEdge() {
+ public void testNoRounding_CutoutShortEdge_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
- true /* fillCutout */);
+ true /* fillCutout */, true /* privacyDot */);
+
+ // top cutout
+ final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
+ doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
+ .when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+ // Top window is created for top cutout.
+ // Bottom window is created for privacy dot.
+ // Left or right window should be null.
+ verifyOverlaysExistAndAdded(false, true, false, true);
+
+ // Top rounded corner views shall exist because of cutout
+ // but be gone because of no rounded corner
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
+ // Bottom rounded corner views shall exist because of privacy dot
+ // but be gone because of no rounded corner
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
+
+ // Privacy dots shall exist but invisible
+ verifyDotViewsVisibility(View.INVISIBLE);
+
+ // Dot controller init
+ verify(mDotViewController, times(1)).initialize(
+ isA(View.class), isA(View.class), isA(View.class), isA(View.class));
+ }
+
+ @Test
+ public void testNoRounding_CutoutLongEdge_NoPrivacyDot() {
+ setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
+ 0 /* roundedPadding */, false /* multipleRadius */,
+ true /* fillCutout */, false /* privacyDot */);
// left cutout
final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
@@ -344,21 +576,50 @@
mScreenDecorations.start();
// Left window is created for left cutout.
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]), any());
- // Bottom window should be null.
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
- // Top window should be null.
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
- // Right window should be null.
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
+ // Bottom, top, or right window should be null.
+ verifyOverlaysExistAndAdded(true, false, false, false);
+
+ // Left rounded corner views shall exist because of cutout
+ // but be gone because of no rounded corner
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_LEFT, View.GONE);
+
+ // Top privacy dots shall not exist because of no privacy
+ verifyDotViewsNullable(true);
+
+ // No dot controller init
+ verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
- public void testRounding_CutoutShortEdge() {
+ public void testNoRounding_CutoutLongEdge_PrivacyDot() {
+ setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
+ 0 /* roundedPadding */, false /* multipleRadius */,
+ true /* fillCutout */, true /* privacyDot */);
+
+ // left cutout
+ final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
+ doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
+ .when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+ // Left window is created for left cutout.
+ // Right window is created for privacy.
+ // Bottom, or top window should be null.
+ verifyOverlaysExistAndAdded(true, false, true, false);
+
+ // Privacy dots shall exist but invisible
+ verifyDotViewsVisibility(View.INVISIBLE);
+
+ // Dot controller init
+ verify(mDotViewController, times(1)).initialize(
+ isA(View.class), isA(View.class), isA(View.class), isA(View.class));
+ }
+
+ @Test
+ public void testRounding_CutoutShortEdge_NoPrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
20 /* roundedPadding */, false /* multipleRadius */,
- true /* fillCutout */);
+ true /* fillCutout */, false /* privacyDot */);
// top cutout
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
@@ -367,22 +628,55 @@
mScreenDecorations.start();
// Top window is created for rounded corner and top cutout.
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
// Bottom window is created for rounded corner.
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
- // Left window should be null.
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
- // Right window should be null.
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
+ // Left, or right window should be null.
+ verifyOverlaysExistAndAdded(false, true, false, true);
+
+ // Rounded corner views shall exist
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+
+ // Top privacy dots shall not exist because of no privacy dot
+ verifyDotViewsNullable(true);
+
+ // No dot controller init
+ verify(mDotViewController, never()).initialize(any(), any(), any(), any());
}
@Test
- public void testRounding_CutoutLongEdge() {
+ public void testRounding_CutoutShortEdge_PrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
20 /* roundedPadding */, false /* multipleRadius */,
- true /* fillCutout */);
+ true /* fillCutout */, true /* privacyDot */);
+
+ // top cutout
+ final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
+ doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
+ .when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+ // Top window is created for rounded corner and top cutout.
+ // Bottom window is created for rounded corner.
+ // Left, or right window should be null.
+ verifyOverlaysExistAndAdded(false, true, false, true);
+
+ // Rounded corner views shall exist
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE);
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE);
+
+ // Top privacy dots shall exist but invisible
+ verifyDotViewsVisibility(View.INVISIBLE);
+
+ // Dot controller init
+ verify(mDotViewController, times(1)).initialize(
+ isA(View.class), isA(View.class), isA(View.class), isA(View.class));
+ }
+
+ @Test
+ public void testRounding_CutoutLongEdge_NoPrivacyDot() {
+ setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
+ 20 /* roundedPadding */, false /* multipleRadius */,
+ true /* fillCutout */, false /* privacyDot */);
// left cutout
final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
@@ -391,22 +685,34 @@
mScreenDecorations.start();
// Left window is created for rounded corner and left cutout.
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]), any());
// Right window is created for rounded corner.
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]), any());
- // Top window should be null.
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
- // Bottom window should be null.
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
+ // Top, or bottom window should be null.
+ verifyOverlaysExistAndAdded(true, false, true, false);
}
@Test
- public void testRounding_CutoutShortAndLongEdge() {
+ public void testRounding_CutoutLongEdge_PrivacyDot() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
20 /* roundedPadding */, false /* multipleRadius */,
- true /* fillCutout */);
+ true /* fillCutout */, true /* privacyDot */);
+
+ // left cutout
+ final Rect[] bounds = {new Rect(0, 50, 1, 60), null, null, null};
+ doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), bounds))
+ .when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+ // Left window is created for rounded corner, left cutout, and privacy.
+ // Right window is created for rounded corner and privacy dot.
+ // Top, or bottom window should be null.
+ verifyOverlaysExistAndAdded(true, false, true, false);
+ }
+
+ @Test
+ public void testRounding_CutoutShortAndLongEdge_NoPrivacyDot() {
+ setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
+ 20 /* roundedPadding */, false /* multipleRadius */,
+ true /* fillCutout */, false /* privacyDot */);
// top and left cutout
final Rect[] bounds = {new Rect(0, 50, 1, 60), new Rect(9, 0, 10, 1), null, null};
@@ -415,23 +721,36 @@
mScreenDecorations.start();
// Top window is created for rounded corner and top cutout.
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
// Bottom window is created for rounded corner.
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]), any());
// Left window is created for left cutout.
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]), any());
// Right window should be null.
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
+ verifyOverlaysExistAndAdded(true, true, false, true);
}
@Test
- public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout() {
+ public void testRounding_CutoutShortAndLongEdge_PrivacyDot() {
+ setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
+ 20 /* roundedPadding */, false /* multipleRadius */,
+ true /* fillCutout */, true /* privacyDot */);
+
+ // top and left cutout
+ final Rect[] bounds = {new Rect(0, 50, 1, 60), new Rect(9, 0, 10, 1), null, null};
+ doReturn(getDisplayCutoutForRotation(Insets.of(1, 1, 0, 0), bounds))
+ .when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+ // Top window is created for rounded corner and top cutout.
+ // Bottom window is created for rounded corner.
+ // Left window is created for left cutout.
+ // Right window should be null.
+ verifyOverlaysExistAndAdded(true, true, false, true);
+ }
+
+ @Test
+ public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout_NoPrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
- true /* fillCutout */);
+ true /* fillCutout */, false /* privacyDot */);
// Set to short edge cutout(top).
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
@@ -439,11 +758,7 @@
.when(mScreenDecorations).getCutout();
mScreenDecorations.start();
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
+ verifyOverlaysExistAndAdded(false, true, false, false);
// Switch to long edge cutout(left).
final Rect[] newBounds = {new Rect(0, 50, 1, 60), null, null, null};
@@ -451,18 +766,37 @@
.when(mScreenDecorations).getCutout();
mScreenDecorations.onConfigurationChanged(new Configuration());
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]), any());
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]);
+ verifyOverlaysExistAndAdded(true, false, false, false);
}
@Test
- public void testDelayedCutout() {
+ public void testNoRounding_SwitchFrom_ShortEdgeCutout_To_LongCutout_PrivacyDot() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
- false /* fillCutout */);
+ true /* fillCutout */, true /* privacyDot */);
+
+ // Set to short edge cutout(top).
+ final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
+ doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
+ .when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+ verifyOverlaysExistAndAdded(false, true, false, true);
+
+ // Switch to long edge cutout(left).
+ final Rect[] newBounds = {new Rect(0, 50, 1, 60), null, null, null};
+ doReturn(getDisplayCutoutForRotation(Insets.of(1, 0, 0, 0), newBounds))
+ .when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.onConfigurationChanged(new Configuration());
+ verifyOverlaysExistAndAdded(true, false, true, false);
+ }
+
+ @Test
+ public void testDelayedCutout_NoPrivacyDot() {
+ setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
+ 0 /* roundedPadding */, false /* multipleRadius */,
+ false /* fillCutout */, false /* privacyDot */);
// top cutout
final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
@@ -478,11 +812,38 @@
mScreenDecorations.onConfigurationChanged(new Configuration());
// Only top windows should be added.
- verify(mWindowManager, times(1))
- .addView(eq(mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP]), any());
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM]);
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT]);
- assertNull(mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT]);
+ verifyOverlaysExistAndAdded(false, true, false, false);
+ }
+
+ @Test
+ public void testDelayedCutout_PrivacyDot() {
+ setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
+ 0 /* roundedPadding */, false /* multipleRadius */,
+ false /* fillCutout */, true /* privacyDot */);
+
+ // top cutout
+ final Rect[] bounds = {null, new Rect(9, 0, 10, 1), null, null};
+ doReturn(getDisplayCutoutForRotation(Insets.of(0, 1, 0, 0), bounds))
+ .when(mScreenDecorations).getCutout();
+
+ mScreenDecorations.start();
+ // Both top and bottom windows should be added because of privacy dot,
+ // but their visibility shall be gone because of no rounding.
+ verifyOverlaysExistAndAdded(false, true, false, true);
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
+
+ when(mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout))
+ .thenReturn(true);
+ mScreenDecorations.onConfigurationChanged(new Configuration());
+
+ assertNotNull(mScreenDecorations.mOverlays);
+ // Both top and bottom windows should be added because of privacy dot,
+ // but their visibility shall be gone because of no rounding.
+ verifyOverlaysExistAndAdded(false, true, false, true);
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE);
+ verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE);
}
@Test
@@ -496,7 +857,7 @@
public void testUpdateRoundedCorners() {
setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
- false /* fillCutout */);
+ false /* fillCutout */, true /* privacyDot */);
mScreenDecorations.start();
assertEquals(mScreenDecorations.mRoundedDefault, new Point(20, 20));
@@ -511,7 +872,7 @@
public void testOnlyRoundedCornerRadiusTop() {
setupResources(0 /* radius */, 10 /* radiusTop */, 0 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
- false /* fillCutout */);
+ false /* fillCutout */, true /* privacyDot */);
mScreenDecorations.start();
assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefault);
@@ -523,7 +884,7 @@
public void testOnlyRoundedCornerRadiusBottom() {
setupResources(0 /* radius */, 0 /* radiusTop */, 20 /* radiusBottom */,
0 /* roundedPadding */, false /* multipleRadius */,
- false /* fillCutout */);
+ false /* fillCutout */, true /* privacyDot */);
mScreenDecorations.start();
assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefault);
@@ -531,7 +892,6 @@
assertEquals(new Point(20, 20), mScreenDecorations.mRoundedDefaultBottom);
}
-
@Test
public void testBoundingRectsToRegion() throws Exception {
Rect rect = new Rect(1, 2, 3, 4);
@@ -588,7 +948,7 @@
}
private void setupResources(int radius, int radiusTop, int radiusBottom, int roundedPadding,
- boolean multipleRadius, boolean fillCutout) {
+ boolean multipleRadius, boolean fillCutout, boolean privacyDot) {
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.array.config_displayUniqueIdArray,
new String[]{});
@@ -625,6 +985,8 @@
R.bool.config_roundedCornerMultipleRadius, multipleRadius);
mContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout);
+ mContext.getOrCreateTestableResources().addOverride(
+ R.bool.config_enablePrivacyDot, privacyDot);
}
private DisplayCutout getDisplayCutoutForRotation(Insets safeInsets, Rect[] cutoutBounds) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index cc35a8f..d819fa2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -46,6 +46,7 @@
@RunWithLooper
class ActivityLaunchAnimatorTest : SysuiTestCase() {
private val launchContainer = LinearLayout(mContext)
+ private val launchAnimator = LaunchAnimator(mContext, isForTesting = true)
@Mock lateinit var callback: ActivityLaunchAnimator.Callback
@Spy private val controller = TestLaunchAnimatorController(launchContainer)
@Mock lateinit var iCallback: IRemoteAnimationFinishedCallback
@@ -56,7 +57,8 @@
@Before
fun setup() {
- activityLaunchAnimator = ActivityLaunchAnimator(callback, mContext)
+ activityLaunchAnimator = ActivityLaunchAnimator(launchAnimator)
+ activityLaunchAnimator.callback = callback
}
private fun startIntentWithAnimation(
@@ -120,7 +122,8 @@
@Test
fun animatesIfActivityIsAlreadyOpenAndIsOnKeyguard() {
`when`(callback.isOnKeyguard()).thenReturn(true)
- val animator = ActivityLaunchAnimator(callback, context)
+ val animator = ActivityLaunchAnimator(launchAnimator)
+ animator.callback = callback
val willAnimateCaptor = ArgumentCaptor.forClass(Boolean::class.java)
var animationAdapter: RemoteAnimationAdapter? = null
@@ -208,7 +211,7 @@
private class TestLaunchAnimatorController(
override var launchContainer: ViewGroup
) : ActivityLaunchAnimator.Controller {
- override fun createAnimatorState() = ActivityLaunchAnimator.State(
+ override fun createAnimatorState() = LaunchAnimator.State(
top = 100,
bottom = 200,
left = 300,
@@ -232,7 +235,7 @@
}
override fun onLaunchAnimationProgress(
- state: ActivityLaunchAnimator.State,
+ state: LaunchAnimator.State,
progress: Float,
linearProgress: Float
) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
new file mode 100644
index 0000000..5bcf828
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -0,0 +1,186 @@
+package com.android.systemui.animation
+
+import android.app.Dialog
+import android.content.Context
+import android.os.Bundle
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.ViewUtils
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.widget.LinearLayout
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class DialogLaunchAnimatorTest : SysuiTestCase() {
+ private val launchAnimator = LaunchAnimator(context, isForTesting = true)
+ private val hostDialogprovider = TestHostDialogProvider()
+ private val dialogLaunchAnimator =
+ DialogLaunchAnimator(context, launchAnimator, hostDialogprovider)
+
+ @Test
+ fun testShowDialogFromView() {
+ // Show the dialog. showFromView() must be called on the main thread with a dialog created
+ // on the main thread too.
+ val (dialog, hostDialog) = runOnMainThreadAndWaitForIdleSync {
+ val touchSurfaceRoot = LinearLayout(context)
+ val touchSurface = View(context)
+ touchSurfaceRoot.addView(touchSurface)
+
+ // We need to attach the root to the window manager otherwise the exit animation will
+ // be skipped
+ ViewUtils.attachView(touchSurfaceRoot)
+
+ val dialog = TestDialog(context)
+ val hostDialog =
+ dialogLaunchAnimator.showFromView(dialog, touchSurface) as TestHostDialog
+ dialog to hostDialog
+ }
+
+ // Only the host dialog is actually showing.
+ assertTrue(hostDialog.isShowing)
+ assertFalse(dialog.isShowing)
+
+ // The dialog onStart() method was called but not onStop().
+ assertTrue(dialog.onStartCalled)
+ assertFalse(dialog.onStopCalled)
+
+ // The dialog content has been stolen and is shown inside the host dialog.
+ val hostDialogContent = hostDialog.findViewById<ViewGroup>(android.R.id.content)
+ assertEquals(0, dialog.findViewById<ViewGroup>(android.R.id.content).childCount)
+ assertEquals(1, hostDialogContent.childCount)
+
+ val hostDialogRoot = hostDialogContent.getChildAt(0) as ViewGroup
+ assertEquals(1, hostDialogRoot.childCount)
+ assertEquals(dialog.contentView, hostDialogRoot.getChildAt(0))
+
+ // If we are dozing, the host dialog window also fades out.
+ runOnMainThreadAndWaitForIdleSync { dialogLaunchAnimator.onDozeAmountChanged(0.5f) }
+ assertTrue(hostDialog.window!!.decorView.alpha < 1f)
+
+ // Hiding/showing/dismissing the dialog should hide/show/dismiss the host dialog given that
+ // it's a ListenableDialog.
+ runOnMainThreadAndWaitForIdleSync { dialog.hide() }
+ assertFalse(hostDialog.isShowing)
+ assertFalse(dialog.isShowing)
+
+ runOnMainThreadAndWaitForIdleSync { dialog.show() }
+ assertTrue(hostDialog.isShowing)
+ assertFalse(dialog.isShowing)
+
+ assertFalse(dialog.onStopCalled)
+ runOnMainThreadAndWaitForIdleSync { dialog.dismiss() }
+ assertFalse(hostDialog.isShowing)
+ assertFalse(dialog.isShowing)
+ assertTrue(hostDialog.wasDismissed)
+ assertTrue(dialog.onStopCalled)
+ }
+
+ private fun <T : Any> runOnMainThreadAndWaitForIdleSync(f: () -> T): T {
+ lateinit var result: T
+ context.mainExecutor.execute {
+ result = f()
+ }
+ waitForIdleSync()
+ return result
+ }
+
+ private class TestHostDialogProvider : HostDialogProvider {
+ override fun createHostDialog(
+ context: Context,
+ theme: Int,
+ onCreateCallback: () -> Unit,
+ dismissOverride: (() -> Unit) -> Unit
+ ): Dialog = TestHostDialog(context, onCreateCallback, dismissOverride)
+ }
+
+ private class TestHostDialog(
+ context: Context,
+ private val onCreateCallback: () -> Unit,
+ private val dismissOverride: (() -> Unit) -> Unit
+ ) : Dialog(context) {
+ var wasDismissed = false
+
+ init {
+ // We need to set the window type for dialogs shown by SysUI, otherwise WM will throw.
+ window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ onCreateCallback()
+ }
+
+ override fun dismiss() {
+ dismissOverride {
+ super.dismiss()
+ wasDismissed = true
+ }
+ }
+ }
+
+ private class TestDialog(context: Context) : Dialog(context), ListenableDialog {
+ private val listeners = hashSetOf<DialogListener>()
+ val contentView = View(context)
+ var onStartCalled = false
+ var onStopCalled = false
+
+ init {
+ // We need to set the window type for dialogs shown by SysUI, otherwise WM will throw.
+ window.setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL)
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(contentView)
+ }
+
+ override fun onStart() {
+ super.onStart()
+ onStartCalled = true
+ }
+
+ override fun onStop() {
+ super.onStart()
+ onStopCalled = true
+ }
+
+ override fun addListener(listener: DialogListener) {
+ listeners.add(listener)
+ }
+
+ override fun removeListener(listener: DialogListener) {
+ listeners.remove(listener)
+ }
+
+ override fun dismiss() {
+ super.dismiss()
+ notifyListeners { onDismiss() }
+ }
+
+ override fun hide() {
+ super.hide()
+ notifyListeners { onHide() }
+ }
+
+ override fun show() {
+ super.show()
+ notifyListeners { onShow() }
+ }
+
+ private fun notifyListeners(notify: DialogListener.() -> Unit) {
+ for (listener in HashSet(listeners)) {
+ listener.notify()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
index 8cba25d..58e0cb2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt
@@ -32,7 +32,7 @@
fun animatingOrphanViewDoesNotCrash() {
val ghostedView = LinearLayout(mContext)
val controller = GhostedViewLaunchAnimatorController(ghostedView)
- val state = ActivityLaunchAnimator.State(top = 0, bottom = 0, left = 0, right = 0)
+ val state = LaunchAnimator.State(top = 0, bottom = 0, left = 0, right = 0)
controller.onIntentStarted(willAnimate = true)
controller.onLaunchAnimationStart(isExpandingFullyAbove = true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index f2f0029..2c4808a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -21,11 +21,12 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
-import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -34,6 +35,8 @@
import com.android.systemui.statusbar.phone.StatusBar
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.leak.RotationUtils
+import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -48,12 +51,15 @@
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-
+import org.mockito.MockitoSession
+import org.mockito.quality.Strictness
import javax.inject.Provider
@SmallTest
@RunWith(AndroidTestingRunner::class)
class AuthRippleControllerTest : SysuiTestCase() {
+ private lateinit var staticMockSession: MockitoSession
+
private lateinit var controller: AuthRippleController
@Mock private lateinit var statusBar: StatusBar
@Mock private lateinit var rippleView: AuthRippleView
@@ -74,6 +80,12 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ staticMockSession = mockitoSession()
+ .mockStatic(RotationUtils::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
+
+ `when`(RotationUtils.getRotation(context)).thenReturn(RotationUtils.ROTATION_NONE)
`when`(udfpsControllerProvider.get()).thenReturn(udfpsController)
controller = AuthRippleController(
@@ -96,6 +108,11 @@
`when`(statusBar.lightRevealScrim).thenReturn(lightRevealScrim)
}
+ @After
+ fun tearDown() {
+ staticMockSession.finishMocking()
+ }
+
@Test
fun testFingerprintTrigger_Ripple() {
// GIVEN fp exists, keyguard is visible, user doesn't need strong auth
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
index d6226aa..866791c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java
@@ -40,7 +40,7 @@
when(params.doubleTapReportsTouchCoordinates()).thenReturn(false);
when(params.getDisplayNeedsBlanking()).thenReturn(false);
when(params.getSelectivelyRegisterSensorsUsingProx()).thenReturn(false);
- when(params.singleTapUsesProx()).thenReturn(true);
+ when(params.singleTapUsesProx(anyInt())).thenReturn(true);
when(params.longPressUsesProx()).thenReturn(true);
when(params.getQuickPickupAodDuration()).thenReturn(500);
@@ -61,14 +61,13 @@
when(config.getWakeLockScreenDebounce()).thenReturn(0L);
when(config.doubleTapSensorType()).thenReturn(null);
- when(config.tapSensorType()).thenReturn(null);
when(config.longPressSensorType()).thenReturn(null);
when(config.udfpsLongPressSensorType()).thenReturn(null);
when(config.quickPickupSensorType()).thenReturn(null);
when(config.tapGestureEnabled(anyInt())).thenReturn(true);
when(config.tapSensorAvailable()).thenReturn(true);
- when(config.tapSensorType()).thenReturn(FakeSensorManager.TAP_SENSOR_TYPE);
+ when(config.tapSensorType(anyInt())).thenReturn(FakeSensorManager.TAP_SENSOR_TYPE);
when(config.dozePickupSensorAvailable()).thenReturn(false);
when(config.wakeScreenGestureAvailable()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 5c4c27c..42e34c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -19,6 +19,7 @@
import static com.android.systemui.doze.DozeLog.REASON_SENSOR_TAP;
import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -47,6 +48,7 @@
import com.android.systemui.doze.DozeSensors.TriggerSensor;
import com.android.systemui.plugins.SensorManagerPlugin;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.settings.FakeSettings;
@@ -58,6 +60,11 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
@RunWith(AndroidTestingRunner.class)
@@ -85,6 +92,8 @@
private AuthController mAuthController;
@Mock
private ProximitySensor mProximitySensor;
+ private @DevicePostureController.DevicePostureInt int mDevicePosture =
+ DevicePostureController.DEVICE_POSTURE_UNKNOWN;
private FakeSettings mFakeSettings = new FakeSettings();
private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
private TestableLooper mTestableLooper;
@@ -101,6 +110,7 @@
((Runnable) invocation.getArgument(0)).run();
return null;
}).when(mWakeLock).wrap(any(Runnable.class));
+ mDevicePosture = DevicePostureController.DEVICE_POSTURE_UNKNOWN;
mDozeSensors = new TestableDozeSensors();
}
@@ -157,7 +167,7 @@
// GIVEN we only should register sensors using prox when not in low-powered mode / off
// and the single tap sensor uses the proximity sensor
when(mDozeParameters.getSelectivelyRegisterSensorsUsingProx()).thenReturn(true);
- when(mDozeParameters.singleTapUsesProx()).thenReturn(true);
+ when(mDozeParameters.singleTapUsesProx(anyInt())).thenReturn(true);
TestableDozeSensors dozeSensors = new TestableDozeSensors();
// THEN on initialization, the tap sensor isn't requested
@@ -258,12 +268,55 @@
assertTrue(triggerSensor.mRegistered);
}
+ @Test
+ public void testPostureOpen_registersCorrectTapGesture() {
+ // GIVEN device posture open
+ mDevicePosture = DevicePostureController.DEVICE_POSTURE_OPENED;
+
+ // WHEN DozeSensors are initialized
+ new TestableDozeSensors();
+
+ // THEN we use the posture to determine which tap sensor to use
+ verify(mAmbientDisplayConfiguration).tapSensorType(eq(mDevicePosture));
+ }
+
+ @Test
+ public void testFindSensor() throws Exception {
+ // GIVEN a prox sensor
+ List<Sensor> sensors = new ArrayList<>();
+ Sensor proxSensor =
+ createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY);
+ sensors.add(proxSensor);
+
+ when(mSensorManager.getSensorList(anyInt())).thenReturn(sensors);
+
+ // WHEN we try to find the prox sensor with the same type and name
+ // THEN we find the added sensor
+ assertEquals(
+ proxSensor,
+ DozeSensors.findSensor(
+ mSensorManager,
+ Sensor.STRING_TYPE_PROXIMITY,
+ proxSensor.getName()));
+
+ // WHEN we try to find a prox sensor with a different name
+ // THEN no sensor is found
+ assertEquals(
+ null,
+ DozeSensors.findSensor(
+ mSensorManager,
+ Sensor.STRING_TYPE_PROXIMITY,
+ "some other name"));
+ }
+
+
private class TestableDozeSensors extends DozeSensors {
TestableDozeSensors() {
super(getContext(), mSensorManager, mDozeParameters,
mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
- mProximitySensor, mFakeSettings, mAuthController);
+ mProximitySensor, mFakeSettings, mAuthController,
+ mDevicePosture);
for (TriggerSensor sensor : mSensors) {
if (sensor instanceof PluginSensor
&& ((PluginSensor) sensor).mPluginSensor.getType()
@@ -288,4 +341,23 @@
mDozeLog);
}
}
+
+ public static void setSensorType(Sensor sensor, int type, String strType) throws Exception {
+ Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+ setter.setAccessible(true);
+ setter.invoke(sensor, type);
+ if (strType != null) {
+ Field f = sensor.getClass().getDeclaredField("mStringType");
+ f.setAccessible(true);
+ f.set(sensor, strType);
+ }
+ }
+
+ public static Sensor createSensor(int type, String strType) throws Exception {
+ Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+ constr.setAccessible(true);
+ Sensor sensor = constr.newInstance();
+ setSensorType(sensor, type, strType);
+ return sensor;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 9577c7a..b688fcc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
@@ -86,6 +87,8 @@
private UiEventLogger mUiEventLogger;
@Mock
private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private DevicePostureController mDevicePostureController;
private DozeTriggers mTriggers;
private FakeSensorManager mSensors;
@@ -117,7 +120,8 @@
mTriggers = new DozeTriggers(mContext, mHost, config, dozeParameters,
asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(),
- mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController);
+ mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController,
+ mDevicePostureController);
mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 47c5545..d864aae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -405,6 +405,26 @@
}
@Test
+ fun testOnSmartspaceMediaDataLoaded_hasNullIntent_callsListener() {
+ val recommendationExtras = Bundle().apply {
+ putString("package_name", PACKAGE_NAME)
+ putParcelable("dismiss_intent", null)
+ }
+ whenever(mediaSmartspaceBaseAction.extras).thenReturn(recommendationExtras)
+ whenever(mediaSmartspaceTarget.baseAction).thenReturn(mediaSmartspaceBaseAction)
+ whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf())
+
+ smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+
+ verify(listener).onSmartspaceMediaDataLoaded(
+ eq(KEY_MEDIA_SMARTSPACE),
+ eq(EMPTY_SMARTSPACE_MEDIA_DATA
+ .copy(targetId = KEY_MEDIA_SMARTSPACE, isActive = true,
+ isValid = false, dismissIntent = null)),
+ eq(false))
+ }
+
+ @Test
fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_notCallsListener() {
smartspaceMediaDataProvider.onTargetsAvailable(listOf())
verify(listener, never())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 2c68661..25ca8c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -54,6 +54,7 @@
// Mock
private MediaOutputController mMediaOutputController = mock(MediaOutputController.class);
+ private MediaOutputDialog mMediaOutputDialog = mock(MediaOutputDialog.class);
private MediaDevice mMediaDevice1 = mock(MediaDevice.class);
private MediaDevice mMediaDevice2 = mock(MediaDevice.class);
private Icon mIcon = mock(Icon.class);
@@ -65,7 +66,7 @@
@Before
public void setUp() {
- mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController);
+ mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController, mMediaOutputDialog);
mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter
.onCreateViewHolder(new LinearLayout(mContext), 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 9bd07b8..053851e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -39,6 +39,7 @@
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -63,6 +64,7 @@
private NotificationEntryManager mNotificationEntryManager =
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
+ private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
private MediaOutputController mMediaOutputController;
@@ -75,7 +77,7 @@
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext,
mMediaOutputController);
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index d1a617b..f7e60ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -49,6 +49,7 @@
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -91,6 +92,7 @@
private NotificationEntryManager mNotificationEntryManager =
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
+ private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
private Context mSpyContext;
private MediaOutputController mMediaOutputController;
@@ -113,7 +115,7 @@
mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
MediaDescription.Builder builder = new MediaDescription.Builder();
@@ -157,7 +159,7 @@
public void start_withoutPackageName_verifyMediaControllerInit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputController.start(mCb);
@@ -178,7 +180,7 @@
public void stop_withoutPackageName_verifyMediaControllerDeinit() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputController.start(mCb);
@@ -449,7 +451,7 @@
public void getNotificationLargeIcon_withoutPackageName_returnsNull() {
mMediaOutputController = new MediaOutputController(mSpyContext, null, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
assertThat(mMediaOutputController.getNotificationIcon()).isNull();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 86f6bde..8a3ea56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -36,6 +36,7 @@
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -65,6 +66,7 @@
private final NotificationEntryManager mNotificationEntryManager =
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
+ private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
private MediaOutputDialog mMediaOutputDialog;
private MediaOutputController mMediaOutputController;
@@ -74,10 +76,11 @@
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputDialog = new MediaOutputDialog(mContext, false,
mMediaOutputController, mUiEventLogger);
+ mMediaOutputDialog.show();
when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice);
when(mMediaDevice.getFeatures()).thenReturn(mFeatures);
@@ -123,6 +126,7 @@
public void onCreate_ShouldLogVisibility() {
MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false,
mMediaOutputController, mUiEventLogger);
+ testDialog.show();
testDialog.dismissDialog();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
index c296ff5..e8cd6c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java
@@ -34,6 +34,7 @@
import com.android.settingslib.media.MediaDevice;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.phone.ShadeController;
@@ -64,6 +65,7 @@
private NotificationEntryManager mNotificationEntryManager =
mock(NotificationEntryManager.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
+ private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
private MediaOutputGroupDialog mMediaOutputGroupDialog;
private MediaOutputController mMediaOutputController;
@@ -73,10 +75,11 @@
public void setUp() {
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, false,
mMediaSessionManager, mLocalBluetoothManager, mShadeController, mStarter,
- mNotificationEntryManager, mUiEventLogger);
+ mNotificationEntryManager, mUiEventLogger, mDialogLaunchAnimator);
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false,
mMediaOutputController);
+ mMediaOutputGroupDialog.show();
when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 83493aa..c1a9739 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -35,23 +35,42 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.util.SparseArray;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.accessibility.SystemActions;
+import com.android.systemui.assist.AssistManager;
+import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.model.SysUiState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
+import com.android.systemui.recents.Recents;
+import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
+import com.android.wm.shell.pip.Pip;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
/** atest NavigationBarControllerTest */
@RunWith(AndroidTestingRunner.class)
@@ -59,31 +78,46 @@
@SmallTest
public class NavigationBarControllerTest extends SysuiTestCase {
- private static final int SECONDARY_DISPLAY = 1;
-
private NavigationBarController mNavigationBarController;
private NavigationBar mDefaultNavBar;
private NavigationBar mSecondaryNavBar;
- @Mock
- private CommandQueue mCommandQueue;
- @Mock
- private NavigationBar.Factory mNavigationBarFactory;
+ private CommandQueue mCommandQueue = mock(CommandQueue.class);
+
+ private static final int SECONDARY_DISPLAY = 1;
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
mNavigationBarController = spy(
new NavigationBarController(mContext,
+ mock(WindowManager.class),
+ () -> mock(AssistManager.class),
+ mock(AccessibilityManager.class),
+ mock(AccessibilityManagerWrapper.class),
+ mock(DeviceProvisionedController.class),
+ mock(MetricsLogger.class),
mock(OverviewProxyService.class),
mock(NavigationModeController.class),
+ mock(AccessibilityButtonModeObserver.class),
+ mock(StatusBarStateController.class),
mock(SysUiState.class),
+ mock(BroadcastDispatcher.class),
mCommandQueue,
+ Optional.of(mock(Pip.class)),
+ Optional.of(mock(LegacySplitScreen.class)),
+ Optional.of(mock(Recents.class)),
+ () -> Optional.of(mock(StatusBar.class)),
+ mock(ShadeController.class),
+ mock(NotificationRemoteInputManager.class),
+ mock(NotificationShadeDepthController.class),
+ mock(SystemActions.class),
Dependency.get(Dependency.MAIN_HANDLER),
+ mock(UiEventLogger.class),
+ mock(NavigationBarOverlayController.class),
mock(ConfigurationController.class),
mock(NavigationBarA11yHelper.class),
mock(TaskbarDelegate.class),
- mNavigationBarFactory,
+ mock(UserTracker.class),
mock(DumpManager.class)));
initializeNavigationBars();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 422db1f..e37f422 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -19,7 +19,6 @@
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
-import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
@@ -29,6 +28,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -45,11 +45,11 @@
import android.content.IntentFilter;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
+import android.os.Looper;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
-import android.telecom.TelecomManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -60,12 +60,12 @@
import android.view.WindowManager;
import android.view.WindowMetrics;
import android.view.accessibility.AccessibilityManager;
-import android.view.inputmethod.InputMethodManager;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
@@ -81,10 +81,9 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.phone.AutoHideController;
-import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -111,13 +110,7 @@
private NavigationBar mExternalDisplayNavigationBar;
private SysuiTestableContext mSysuiTestableContextExternal;
- @Mock
private OverviewProxyService mOverviewProxyService;
- @Mock
- private StatusBarStateController mStatusBarStateController;
- @Mock
- private NavigationModeController mNavigationModeController;
- @Mock
private CommandQueue mCommandQueue;
private SysUiState mMockSysUiState;
@Mock
@@ -132,25 +125,11 @@
EdgeBackGestureHandler mEdgeBackGestureHandler;
@Mock
NavigationBarA11yHelper mNavigationBarA11yHelper;
- @Mock
- private LightBarController mLightBarController;
- @Mock
- private LightBarController.Factory mLightBarcontrollerFactory;
- @Mock
- private AutoHideController mAutoHideController;
- @Mock
- private AutoHideController.Factory mAutoHideControllerFactory;
- @Mock
- private WindowManager mWindowManager;
- @Mock
- private TelecomManager mTelecomManager;
- @Mock
- private InputMethodManager mInputMethodManager;
- @Mock
- private AssistManager mAssistManager;
@Rule
public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
+ private AccessibilityManagerWrapper mAccessibilityWrapper =
+ new AccessibilityManagerWrapper(mContext);
@Before
public void setup() throws Exception {
@@ -158,19 +137,15 @@
when(mEdgeBackGestureHandlerFactory.create(any(Context.class)))
.thenReturn(mEdgeBackGestureHandler);
- when(mLightBarcontrollerFactory.create(any(Context.class))).thenReturn(mLightBarController);
- when(mAutoHideControllerFactory.create(any(Context.class))).thenReturn(mAutoHideController);
+ mCommandQueue = new CommandQueue(mContext);
setupSysuiDependency();
- // This class inflates views that call Dependency.get, thus these injections are still
- // necessary.
- mDependency.injectTestDependency(AssistManager.class, mAssistManager);
+ mDependency.injectMockDependency(AssistManager.class);
mDependency.injectMockDependency(KeyguardStateController.class);
- mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController);
+ mDependency.injectMockDependency(StatusBarStateController.class);
mDependency.injectMockDependency(NavigationBarController.class);
+ mOverviewProxyService = mDependency.injectMockDependency(OverviewProxyService.class);
mDependency.injectTestDependency(EdgeBackGestureHandler.Factory.class,
mEdgeBackGestureHandlerFactory);
- mDependency.injectTestDependency(OverviewProxyService.class, mOverviewProxyService);
- mDependency.injectTestDependency(NavigationModeController.class, mNavigationModeController);
TestableLooper.get(this).runWithLooper(() -> {
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
@@ -189,13 +164,23 @@
mSysuiTestableContextExternal = (SysuiTestableContext) getContext().createDisplayContext(
display);
- Display defaultDisplay = mContext.getDisplay();
- when(mWindowManager.getDefaultDisplay()).thenReturn(defaultDisplay);
- WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
+ WindowManager windowManager = mock(WindowManager.class);
+ Display defaultDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
+ when(windowManager.getDefaultDisplay()).thenReturn(
+ defaultDisplay);
+ WindowMetrics maximumWindowMetrics = mContext.getSystemService(WindowManager.class)
.getMaximumWindowMetrics();
- when(mWindowManager.getMaximumWindowMetrics()).thenReturn(metrics);
- doNothing().when(mWindowManager).addView(any(), any());
- doNothing().when(mWindowManager).removeViewImmediate(any());
+ when(windowManager.getMaximumWindowMetrics()).thenReturn(maximumWindowMetrics);
+ WindowMetrics currentWindowMetrics = mContext.getSystemService(WindowManager.class)
+ .getCurrentWindowMetrics();
+ when(windowManager.getCurrentWindowMetrics()).thenReturn(currentWindowMetrics);
+ doNothing().when(windowManager).addView(any(), any());
+ mContext.addMockSystemService(Context.WINDOW_SERVICE, windowManager);
+ mSysuiTestableContextExternal.addMockSystemService(Context.WINDOW_SERVICE, windowManager);
+
+ mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
+ mDependency.injectTestDependency(AccessibilityManagerWrapper.class, mAccessibilityWrapper);
+
mMockSysUiState = mock(SysUiState.class);
when(mMockSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mMockSysUiState);
}
@@ -254,8 +239,10 @@
defaultNavBar.createView(null);
externalNavBar.createView(null);
- defaultNavBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
- BACK_DISPOSITION_DEFAULT, true);
+ // Set IME window status for default NavBar.
+ mCommandQueue.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
+ BACK_DISPOSITION_DEFAULT, true, false);
+ processAllMessages();
// Verify IME window state will be updated in default NavBar & external NavBar state reset.
assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN,
@@ -263,10 +250,11 @@
assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
- externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null, IME_VISIBLE,
- BACK_DISPOSITION_DEFAULT, true);
- defaultNavBar.setImeWindowStatus(
- DEFAULT_DISPLAY, null, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT, false);
+ // Set IME window status for external NavBar.
+ mCommandQueue.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null,
+ IME_VISIBLE, BACK_DISPOSITION_DEFAULT, true, false);
+ processAllMessages();
+
// Verify IME window state will be updated in external NavBar & default NavBar state reset.
assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN,
externalNavBar.getNavigationIconHints());
@@ -292,16 +280,19 @@
DeviceProvisionedController deviceProvisionedController =
mock(DeviceProvisionedController.class);
when(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
- NavigationBar.Factory factory = new NavigationBar.Factory(
- mWindowManager,
- () -> mAssistManager,
+ assertNotNull(mAccessibilityWrapper);
+ return spy(new NavigationBar(context,
+ mock(WindowManager.class),
+ () -> mock(AssistManager.class),
mock(AccessibilityManager.class),
+ context.getDisplayId() == DEFAULT_DISPLAY ? mAccessibilityWrapper
+ : mock(AccessibilityManagerWrapper.class),
deviceProvisionedController,
new MetricsLogger(),
mOverviewProxyService,
- mNavigationModeController,
+ mock(NavigationModeController.class),
mock(AccessibilityButtonModeObserver.class),
- mStatusBarStateController,
+ mock(StatusBarStateController.class),
mMockSysUiState,
mBroadcastDispatcher,
mCommandQueue,
@@ -317,14 +308,7 @@
mock(NavigationBarOverlayController.class),
mUiEventLogger,
mNavigationBarA11yHelper,
- mock(UserTracker.class),
- mLightBarController,
- mLightBarcontrollerFactory,
- mAutoHideController,
- mAutoHideControllerFactory,
- Optional.of(mTelecomManager),
- mInputMethodManager);
- return spy(factory.create(context));
+ mock(UserTracker.class)));
}
private void processAllMessages() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index a1b7210..8ae7100 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -39,8 +39,8 @@
import com.android.keyguard.CarrierTextManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import com.android.systemui.utils.os.FakeHandler;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index e939411..f0bd0657 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -43,12 +43,12 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.CastController.CastDevice;
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
import org.junit.Before;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index f9d5be6..fe32839 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -48,9 +48,9 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.AccessPointController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.AccessPointController;
import com.android.systemui.toast.SystemUIToast;
import com.android.systemui.toast.ToastFactory;
import com.android.systemui.util.CarrierConfigTracker;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index af624ed..2416132 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -15,8 +15,6 @@
package com.android.systemui.statusbar;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
-import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
-import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -190,13 +188,8 @@
@Test
public void testShowImeButtonForSecondaryDisplay() {
- // First show in default display to update the "last updated ime display"
- testShowImeButton();
-
mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, null, 1, 2, true, false);
waitForIdleSync();
- verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(null), eq(IME_INVISIBLE),
- eq(BACK_DISPOSITION_DEFAULT), eq(false));
verify(mCallbacks).setImeWindowStatus(
eq(SECONDARY_DISPLAY), eq(null), eq(1), eq(2), eq(true));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
index 4068f93..7896a26 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/AccessPointControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy
+package com.android.systemui.statusbar.connectivity
import android.os.UserManager
import android.test.suitebuilder.annotation.SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
similarity index 91%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
index 3c55df9..11a53c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/CallbackHandlerTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
@@ -28,11 +28,11 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.NetworkController.EmergencyListener;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import com.android.systemui.tests.R;
import org.junit.Before;
@@ -182,7 +182,8 @@
@Test
public void testSignalCallback_setIsAirplaneMode() {
- IconState state = new IconState(true, R.drawable.stat_sys_airplane_mode, "Test Description");
+ IconState state =
+ new IconState(true, R.drawable.stat_sys_airplane_mode, "Test Description");
mHandler.setIsAirplaneMode(state);
waitForCallbacks();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index c488ee9..67cab74 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
@@ -72,10 +72,11 @@
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.MobileDataIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.statusbar.policy.NetworkController.MobileDataIndicators;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -268,7 +269,7 @@
setSubscriptions(mSubId);
mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId);
ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackArg =
- ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
verify(mMockCm, atLeastOnce())
.registerDefaultNetworkCallback(callbackArg.capture(), isA(Handler.class));
int captureSize = callbackArg.getAllValues().size();
@@ -404,7 +405,7 @@
}
private static void setConnectivityCommon(NetworkCapabilities.Builder builder,
- int networkType, boolean validated, boolean isConnected){
+ int networkType, boolean validated, boolean isConnected) {
// TODO: Separate out into several NetworkCapabilities.
if (isConnected) {
builder.addTransportType(networkType);
@@ -538,7 +539,7 @@
}
protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon,
- boolean roaming, boolean inet) {
+ boolean roaming, boolean inet) {
ArgumentCaptor<MobileDataIndicators> indicatorsArg =
ArgumentCaptor.forClass(MobileDataIndicators.class);
@@ -646,7 +647,7 @@
}
protected void assertNetworkNameEquals(String expected) {
- assertEquals("Network name", expected, mMobileSignalController.getState().networkName);
+ assertEquals("Network name", expected, mMobileSignalController.getState().networkName);
}
protected void assertDataNetworkNameEquals(String expected) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index 3433a14..00dedd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -1,4 +1,20 @@
-package com.android.systemui.statusbar.policy;
+/*
+ * 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.systemui.statusbar.connectivity;
import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_WWAN;
import static android.telephony.NetworkRegistrationInfo.DOMAIN_PS;
@@ -22,6 +38,7 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.CarrierConfigTracker;
import org.junit.Test;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
similarity index 71%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
index 6aab9c7..675d755 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerEthernetTest.java
@@ -1,4 +1,20 @@
-package com.android.systemui.statusbar.policy;
+/*
+ * 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.systemui.statusbar.connectivity;
import static junit.framework.Assert.assertEquals;
@@ -7,7 +23,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
-import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.connectivity.NetworkController.IconState;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
similarity index 78%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index 4ff1301..73eddd1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.statusbar.connectivity;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -42,6 +42,7 @@
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.CarrierConfigTracker;
import org.junit.Test;
@@ -280,7 +281,7 @@
// TODO: Put this somewhere else, maybe in its own file.
@Test
public void testHasCorrectMobileControllers() {
- int[] testSubscriptions = new int[] { 1, 5, 3 };
+ int[] testSubscriptions = new int[]{1, 5, 3};
int notTestSubscription = 0;
MobileSignalController mobileSignalController = Mockito.mock(MobileSignalController.class);
@@ -312,8 +313,8 @@
// We will not add one subscription to make sure it's controller gets removed.
int indexToSkipSubscription = 1;
- int[] testSubscriptions = new int[] { 1, 5, 3 };
- MobileSignalController[] mobileSignalControllers = new MobileSignalController[] {
+ int[] testSubscriptions = new int[]{1, 5, 3};
+ MobileSignalController[] mobileSignalControllers = new MobileSignalController[]{
Mockito.mock(MobileSignalController.class),
Mockito.mock(MobileSignalController.class),
Mockito.mock(MobileSignalController.class),
@@ -401,24 +402,24 @@
@Test
public void testOnReceive_stringsUpdatedAction_bothFalse() {
Intent intent = createStringsUpdatedIntent(false /* showSpn */,
- "Irrelevant" /* spn */,
- false /* showPlmn */,
- "Irrelevant" /* plmn */);
+ "Irrelevant" /* spn */,
+ false /* showPlmn */,
+ "Irrelevant" /* plmn */);
mNetworkController.onReceive(mContext, intent);
String defaultNetworkName = mMobileSignalController
.getTextIfExists(
- com.android.internal.R.string.lockscreen_carrier_default).toString();
+ com.android.internal.R.string.lockscreen_carrier_default).toString();
assertNetworkNameEquals(defaultNetworkName);
}
@Test
public void testOnReceive_stringsUpdatedAction_bothTrueAndNull() {
Intent intent = createStringsUpdatedIntent(true /* showSpn */,
- null /* spn */,
- true /* showPlmn */,
- null /* plmn */);
+ null /* spn */,
+ true /* showPlmn */,
+ null /* plmn */);
mNetworkController.onReceive(mContext, intent);
@@ -433,15 +434,15 @@
String plmn = "Test2";
Intent intent = createStringsUpdatedIntent(true /* showSpn */,
- spn /* spn */,
- true /* showPlmn */,
- plmn /* plmn */);
+ spn /* spn */,
+ true /* showPlmn */,
+ plmn /* plmn */);
mNetworkController.onReceive(mContext, intent);
assertNetworkNameEquals(plmn
+ mMobileSignalController.getTextIfExists(
- R.string.status_bar_network_name_separator).toString()
+ R.string.status_bar_network_name_separator).toString()
+ spn);
}
@@ -477,145 +478,149 @@
@Test
public void testOnUpdateDataActivity_dataOut() {
- setupDefaultSignal();
+ setupDefaultSignal();
- updateDataActivity(TelephonyManager.DATA_ACTIVITY_OUT);
+ updateDataActivity(TelephonyManager.DATA_ACTIVITY_OUT);
- verifyLastQsMobileDataIndicators(true /* visible */,
- DEFAULT_LEVEL /* icon */,
- DEFAULT_QS_ICON /* typeIcon */,
- false /* dataIn */,
- true /* dataOut */);
+ verifyLastQsMobileDataIndicators(true /* visible */,
+ DEFAULT_LEVEL /* icon */,
+ DEFAULT_QS_ICON /* typeIcon */,
+ false /* dataIn */,
+ true /* dataOut */);
}
@Test
public void testOnUpdateDataActivity_dataInOut() {
- setupDefaultSignal();
+ setupDefaultSignal();
- updateDataActivity(TelephonyManager.DATA_ACTIVITY_INOUT);
+ updateDataActivity(TelephonyManager.DATA_ACTIVITY_INOUT);
- verifyLastQsMobileDataIndicators(true /* visible */,
- DEFAULT_LEVEL /* icon */,
- DEFAULT_QS_ICON /* typeIcon */,
- true /* dataIn */,
- true /* dataOut */);
+ verifyLastQsMobileDataIndicators(true /* visible */,
+ DEFAULT_LEVEL /* icon */,
+ DEFAULT_QS_ICON /* typeIcon */,
+ true /* dataIn */,
+ true /* dataOut */);
}
@Test
public void testOnUpdateDataActivity_dataActivityNone() {
- setupDefaultSignal();
+ setupDefaultSignal();
- updateDataActivity(TelephonyManager.DATA_ACTIVITY_NONE);
+ updateDataActivity(TelephonyManager.DATA_ACTIVITY_NONE);
- verifyLastQsMobileDataIndicators(true /* visible */,
- DEFAULT_LEVEL /* icon */,
- DEFAULT_QS_ICON /* typeIcon */,
- false /* dataIn */,
- false /* dataOut */);
+ verifyLastQsMobileDataIndicators(true /* visible */,
+ DEFAULT_LEVEL /* icon */,
+ DEFAULT_QS_ICON /* typeIcon */,
+ false /* dataIn */,
+ false /* dataOut */);
}
@Test
public void testCarrierNetworkChange_carrierNetworkChange() {
- int strength = SignalStrength.SIGNAL_STRENGTH_GREAT;
+ int strength = SignalStrength.SIGNAL_STRENGTH_GREAT;
- setupDefaultSignal();
- setLevel(strength);
+ setupDefaultSignal();
+ setLevel(strength);
- // Verify baseline
- verifyLastMobileDataIndicators(true /* visible */,
- strength /* strengthIcon */,
- DEFAULT_ICON /* typeIcon */);
+ // Verify baseline
+ verifyLastMobileDataIndicators(true /* visible */,
+ strength /* strengthIcon */,
+ DEFAULT_ICON /* typeIcon */);
- // API call is made
- setCarrierNetworkChange(true /* enabled */);
+ // API call is made
+ setCarrierNetworkChange(true /* enabled */);
- // Carrier network change is true, show special indicator
- verifyLastMobileDataIndicators(true /* visible */,
- SignalDrawable.getCarrierChangeState(CellSignalStrength.getNumSignalStrengthLevels()),
- 0 /* typeIcon */);
+ // Carrier network change is true, show special indicator
+ verifyLastMobileDataIndicators(true /* visible */,
+ SignalDrawable.getCarrierChangeState(
+ CellSignalStrength.getNumSignalStrengthLevels()),
+ 0 /* typeIcon */);
- // Revert back
- setCarrierNetworkChange(false /* enabled */);
+ // Revert back
+ setCarrierNetworkChange(false /* enabled */);
- // Verify back in previous state
- verifyLastMobileDataIndicators(true /* visible */,
- strength /* strengthIcon */,
- DEFAULT_ICON /* typeIcon */);
+ // Verify back in previous state
+ verifyLastMobileDataIndicators(true /* visible */,
+ strength /* strengthIcon */,
+ DEFAULT_ICON /* typeIcon */);
}
@Test
public void testCarrierNetworkChange_roamingBeforeNetworkChange() {
- int strength = SignalStrength.SIGNAL_STRENGTH_GREAT;
+ int strength = SignalStrength.SIGNAL_STRENGTH_GREAT;
- setupDefaultSignal();
- setLevel(strength);
- setGsmRoaming(true);
+ setupDefaultSignal();
+ setLevel(strength);
+ setGsmRoaming(true);
- // Verify baseline
- verifyLastMobileDataIndicators(true /* visible */,
- strength /* strengthIcon */,
- DEFAULT_ICON /* typeIcon */,
- true /* roaming */);
+ // Verify baseline
+ verifyLastMobileDataIndicators(true /* visible */,
+ strength /* strengthIcon */,
+ DEFAULT_ICON /* typeIcon */,
+ true /* roaming */);
- // API call is made
- setCarrierNetworkChange(true /* enabled */);
+ // API call is made
+ setCarrierNetworkChange(true /* enabled */);
- // Carrier network change is true, show special indicator, no roaming.
- verifyLastMobileDataIndicators(true /* visible */,
- SignalDrawable.getCarrierChangeState(CellSignalStrength.getNumSignalStrengthLevels()),
- 0 /* typeIcon */,
- false /* roaming */);
+ // Carrier network change is true, show special indicator, no roaming.
+ verifyLastMobileDataIndicators(true /* visible */,
+ SignalDrawable.getCarrierChangeState(
+ CellSignalStrength.getNumSignalStrengthLevels()),
+ 0 /* typeIcon */,
+ false /* roaming */);
- // Revert back
- setCarrierNetworkChange(false /* enabled */);
+ // Revert back
+ setCarrierNetworkChange(false /* enabled */);
- // Verify back in previous state
- verifyLastMobileDataIndicators(true /* visible */,
- strength /* strengthIcon */,
- DEFAULT_ICON /* typeIcon */,
- true /* roaming */);
+ // Verify back in previous state
+ verifyLastMobileDataIndicators(true /* visible */,
+ strength /* strengthIcon */,
+ DEFAULT_ICON /* typeIcon */,
+ true /* roaming */);
}
@Test
public void testCarrierNetworkChange_roamingAfterNetworkChange() {
- int strength = SignalStrength.SIGNAL_STRENGTH_GREAT;
+ int strength = SignalStrength.SIGNAL_STRENGTH_GREAT;
- setupDefaultSignal();
- setLevel(strength);
+ setupDefaultSignal();
+ setLevel(strength);
- // Verify baseline
- verifyLastMobileDataIndicators(true /* visible */,
- strength /* strengthIcon */,
- DEFAULT_ICON /* typeIcon */,
- false /* roaming */);
+ // Verify baseline
+ verifyLastMobileDataIndicators(true /* visible */,
+ strength /* strengthIcon */,
+ DEFAULT_ICON /* typeIcon */,
+ false /* roaming */);
- // API call is made
- setCarrierNetworkChange(true /* enabled */);
+ // API call is made
+ setCarrierNetworkChange(true /* enabled */);
- // Carrier network change is true, show special indicator, no roaming.
- verifyLastMobileDataIndicators(true /* visible */,
- SignalDrawable.getCarrierChangeState(CellSignalStrength.getNumSignalStrengthLevels()),
- 0 /* typeIcon */,
- false /* roaming */);
+ // Carrier network change is true, show special indicator, no roaming.
+ verifyLastMobileDataIndicators(true /* visible */,
+ SignalDrawable.getCarrierChangeState(
+ CellSignalStrength.getNumSignalStrengthLevels()),
+ 0 /* typeIcon */,
+ false /* roaming */);
- setGsmRoaming(true);
+ setGsmRoaming(true);
- // Roaming should not show.
- verifyLastMobileDataIndicators(true /* visible */,
- SignalDrawable.getCarrierChangeState(CellSignalStrength.getNumSignalStrengthLevels()),
- 0 /* typeIcon */,
- false /* roaming */);
+ // Roaming should not show.
+ verifyLastMobileDataIndicators(true /* visible */,
+ SignalDrawable.getCarrierChangeState(
+ CellSignalStrength.getNumSignalStrengthLevels()),
+ 0 /* typeIcon */,
+ false /* roaming */);
- // Revert back
- setCarrierNetworkChange(false /* enabled */);
+ // Revert back
+ setCarrierNetworkChange(false /* enabled */);
- // Verify back in previous state
- verifyLastMobileDataIndicators(true /* visible */,
- strength /* strengthIcon */,
- DEFAULT_ICON /* typeIcon */,
- true /* roaming */);
+ // Verify back in previous state
+ verifyLastMobileDataIndicators(true /* visible */,
+ strength /* strengthIcon */,
+ DEFAULT_ICON /* typeIcon */,
+ true /* roaming */);
}
private void verifyEmergencyOnly(boolean isEmergencyOnly) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index 4a5770d..ffeaf20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -1,10 +1,25 @@
-package com.android.systemui.statusbar.policy;
+/*
+ * 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.systemui.statusbar.connectivity;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -21,7 +36,7 @@
import android.testing.TestableLooper.RunWithLooper;
import com.android.settingslib.mobile.TelephonyIcons;
-import com.android.systemui.statusbar.policy.NetworkController.WifiIndicators;
+import com.android.systemui.statusbar.connectivity.NetworkController.WifiIndicators;
import org.junit.Before;
import org.junit.Test;
@@ -165,7 +180,7 @@
}
@Test
- public void testWifiIconDisconnectedViaCallback(){
+ public void testWifiIconDisconnectedViaCallback() {
// Setup normal connection
String testSsid = "Test SSID";
int testLevel = 2;
@@ -183,7 +198,7 @@
}
@Test
- public void testVpnWithUnderlyingWifi(){
+ public void testVpnWithUnderlyingWifi() {
String testSsid = "Test SSID";
int testLevel = 2;
setWifiEnabled(true);
@@ -299,7 +314,7 @@
protected void setWifiLevel(int level) {
float amountPerLevel = (MAX_RSSI - MIN_RSSI) / (WifiIcons.WIFI_LEVEL_COUNT - 1);
- int rssi = (int)(MIN_RSSI + level * amountPerLevel);
+ int rssi = (int) (MIN_RSSI + level * amountPerLevel);
// Put RSSI in the middle of the range.
rssi += amountPerLevel / 2;
when(mWifiInfo.getRssi()).thenReturn(rssi);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index a2bb0af..5d50485 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -44,6 +44,8 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener
import com.android.systemui.util.concurrency.FakeExecution
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
@@ -51,7 +53,6 @@
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.time.FakeSystemClock
-import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentCaptor
@@ -90,6 +91,8 @@
@Mock
private lateinit var statusBarStateController: StatusBarStateController
@Mock
+ private lateinit var deviceProvisionedController: DeviceProvisionedController
+ @Mock
private lateinit var handler: Handler
@Mock
@@ -107,12 +110,17 @@
private lateinit var configChangeListenerCaptor: ArgumentCaptor<ConfigurationListener>
@Captor
private lateinit var statusBarStateListenerCaptor: ArgumentCaptor<StateListener>
+ @Captor
+ private lateinit var deviceProvisionedCaptor: ArgumentCaptor<DeviceProvisionedListener>
private lateinit var sessionListener: OnTargetsAvailableListener
private lateinit var userListener: UserTracker.Callback
private lateinit var settingsObserver: ContentObserver
private lateinit var configChangeListener: ConfigurationListener
private lateinit var statusBarStateListener: StateListener
+ private lateinit var deviceProvisionedListener: DeviceProvisionedListener
+
+ private lateinit var smartspaceView: SmartspaceView
private val clock = FakeSystemClock()
private val executor = FakeExecutor(clock)
@@ -141,9 +149,11 @@
`when`(secureSettings.getUriFor(PRIVATE_LOCKSCREEN_SETTING))
.thenReturn(fakePrivateLockscreenSettingUri)
`when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(smartspaceSession)
- `when`(plugin.getView(any())).thenReturn(fakeSmartspaceView)
+ `when`(plugin.getView(any())).thenReturn(createSmartspaceView(), createSmartspaceView())
`when`(userTracker.userProfiles).thenReturn(userList)
`when`(statusBarStateController.dozeAmount).thenReturn(0.5f)
+ `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
+ `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
setActiveUser(userHandlePrimary)
setAllowPrivateNotifications(userHandlePrimary, true)
@@ -161,11 +171,15 @@
contentResolver,
configurationController,
statusBarStateController,
+ deviceProvisionedController,
execution,
executor,
handler,
Optional.of(plugin)
)
+
+ verify(deviceProvisionedController).addCallback(capture(deviceProvisionedCaptor))
+ deviceProvisionedListener = deviceProvisionedCaptor.value
}
@Test(expected = RuntimeException::class)
@@ -180,6 +194,28 @@
}
@Test
+ fun connectOnlyAfterDeviceIsProvisioned() {
+ // GIVEN an unprovisioned device and an attempt to connect
+ `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(false)
+ `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(false)
+
+ // WHEN a connection attempt is made and view is attached
+ val view = controller.buildAndConnectView(fakeParent)
+ controller.stateChangeListener.onViewAttachedToWindow(view)
+
+ // THEN no session is created
+ verify(smartspaceManager, never()).createSmartspaceSession(any())
+
+ // WHEN it does become provisioned
+ `when`(deviceProvisionedController.isDeviceProvisioned()).thenReturn(true)
+ `when`(deviceProvisionedController.isCurrentUserSetup()).thenReturn(true)
+ deviceProvisionedListener.onUserSetupChanged()
+
+ // THEN the session is created
+ verify(smartspaceManager).createSmartspaceSession(any())
+ }
+
+ @Test
fun testListenersAreRegistered() {
// GIVEN a listener is added after a session is created
connectSession()
@@ -249,7 +285,7 @@
configChangeListener.onThemeChanged()
// We update the new text color to match the wallpaper color
- verify(fakeSmartspaceView).setPrimaryTextColor(anyInt())
+ verify(smartspaceView).setPrimaryTextColor(anyInt())
}
@Test
@@ -261,7 +297,7 @@
statusBarStateListener.onDozeAmountChanged(0.1f, 0.7f)
// We pass that along to the view
- verify(fakeSmartspaceView).setDozeAmount(0.7f)
+ verify(smartspaceView).setDozeAmount(0.7f)
}
@Test
@@ -393,40 +429,30 @@
}
@Test
- fun testBuildViewIsIdempotent() {
- // GIVEN a connected session
- connectSession()
- clearInvocations(plugin)
-
- // WHEN we disconnect and then reconnect
- controller.disconnect()
- controller.buildAndConnectView(fakeParent)
-
- // THEN the view is not rebuilt
- verify(plugin, never()).getView(any())
- assertEquals(fakeSmartspaceView, controller.view)
- }
-
- @Test
- fun testDoubleConnectIsIgnored() {
+ fun testMultipleViewsUseSameSession() {
// GIVEN a connected session
connectSession()
clearInvocations(smartspaceManager)
clearInvocations(plugin)
- // WHEN we're asked to connect a second time and add to a parent
+ // WHEN we're asked to connect a second time and add to a parent. If the same view
+ // was created the ViewGroup will throw an exception
val view = controller.buildAndConnectView(fakeParent)
fakeParent.addView(view)
+ val smartspaceView2 = view as SmartspaceView
- // THEN the existing view and session are reused
+ // THEN the existing session is reused and views are registered
verify(smartspaceManager, never()).createSmartspaceSession(any())
- verify(plugin, never()).getView(any())
- assertEquals(fakeSmartspaceView, controller.view)
+ verify(smartspaceView2).registerDataProvider(plugin)
}
private fun connectSession() {
- controller.buildAndConnectView(fakeParent)
+ val view = controller.buildAndConnectView(fakeParent)
+ smartspaceView = view as SmartspaceView
+ controller.stateChangeListener.onViewAttachedToWindow(view)
+
+ verify(smartspaceView).registerDataProvider(plugin)
verify(smartspaceSession)
.addOnTargetsAvailableListener(any(), capture(sessionListenerCaptor))
sessionListener = sessionListenerCaptor.value
@@ -450,11 +476,11 @@
verify(smartspaceSession).requestSmartspaceUpdate()
clearInvocations(smartspaceSession)
- verify(fakeSmartspaceView).setPrimaryTextColor(anyInt())
- verify(fakeSmartspaceView).setDozeAmount(0.5f)
- clearInvocations(fakeSmartspaceView)
+ verify(smartspaceView).setPrimaryTextColor(anyInt())
+ verify(smartspaceView).setDozeAmount(0.5f)
+ clearInvocations(view)
- fakeParent.addView(fakeSmartspaceView)
+ fakeParent.addView(view)
}
private fun setActiveUser(userHandle: UserHandle) {
@@ -490,31 +516,33 @@
).thenReturn(if (value) 1 else 0)
}
- private val fakeSmartspaceView = spy(object : View(context), SmartspaceView {
- override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
- }
+ private fun createSmartspaceView(): SmartspaceView {
+ return spy(object : View(context), SmartspaceView {
+ override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {
+ }
- override fun setPrimaryTextColor(color: Int) {
- }
+ override fun setPrimaryTextColor(color: Int) {
+ }
- override fun setDozeAmount(amount: Float) {
- }
+ override fun setDozeAmount(amount: Float) {
+ }
- override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {
- }
+ override fun setIntentStarter(intentStarter: BcSmartspaceDataPlugin.IntentStarter?) {
+ }
- override fun setFalsingManager(falsingManager: FalsingManager?) {
- }
+ override fun setFalsingManager(falsingManager: FalsingManager?) {
+ }
- override fun setDnd(image: Drawable?, description: String?) {
- }
+ override fun setDnd(image: Drawable?, description: String?) {
+ }
- override fun setNextAlarm(image: Drawable?, description: String?) {
- }
+ override fun setNextAlarm(image: Drawable?, description: String?) {
+ }
- override fun setMediaTarget(target: SmartspaceTarget?) {
- }
- })
+ override fun setMediaTarget(target: SmartspaceTarget?) {
+ }
+ })
+ }
}
private const val PRIVATE_LOCKSCREEN_SETTING =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 1f3e1c7..902d115 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -32,6 +32,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
@@ -81,6 +82,7 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -94,6 +96,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -120,6 +123,7 @@
@Mock private KeyguardEnvironment mEnvironment;
@Mock private ExpandableNotificationRow mRow;
@Mock private NotificationEntryListener mEntryListener;
+ @Mock private NotifCollectionListener mNotifCollectionListener;
@Mock private NotificationRemoveInterceptor mRemoveInterceptor;
@Mock private HeadsUpManager mHeadsUpManager;
@Mock private RankingMap mRankingMap;
@@ -215,6 +219,7 @@
mEnvironment));
mEntryManager.setUpWithPresenter(mPresenter);
mEntryManager.addNotificationEntryListener(mEntryListener);
+ mEntryManager.addCollectionListener(mNotifCollectionListener);
mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor);
setUserSentiment(mSbn.getKey(), Ranking.USER_SENTIMENT_NEUTRAL);
@@ -318,13 +323,20 @@
eq(mEntry), any(), eq(false) /* removedByUser */, eq(UNDEFINED_DISMISS_REASON));
}
+ /** Regression test for b/201097913. */
@Test
- public void testRemoveNotification_whilePending() {
+ public void testRemoveNotification_whilePending_onlyCollectionListenerNotified() {
+ // Add and then remove a pending entry (entry that hasn't been inflated).
mEntryManager.addNotification(mSbn, mRankingMap);
mEntryManager.removeNotification(mSbn.getKey(), mRankingMap, UNDEFINED_DISMISS_REASON);
+ // Verify that only the listener for the NEW pipeline is notified.
+ // Old pipeline:
verify(mEntryListener, never()).onEntryRemoved(
- eq(mEntry), any(), eq(false /* removedByUser */), eq(UNDEFINED_DISMISS_REASON));
+ argThat(matchEntryOnSbn()), any(), anyBoolean(), anyInt());
+ // New pipeline:
+ verify(mNotifCollectionListener).onEntryRemoved(
+ argThat(matchEntryOnSbn()), anyInt());
}
@Test
@@ -639,6 +651,11 @@
PendingIntent.FLAG_IMMUTABLE)).build();
}
+ // TODO(b/201321631): Update more tests to use this function instead of eq(mEntry).
+ private ArgumentMatcher<NotificationEntry> matchEntryOnSbn() {
+ return e -> e.getSbn().equals(mSbn);
+ }
+
private static class FakeNotificationLifetimeExtender implements NotificationLifetimeExtender {
private NotificationSafeToRemoveCallback mCallback;
private boolean mExtendLifetimes = true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index 48cdd42..be54a6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -45,10 +45,10 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.DisableFlagsLogger;
import com.android.systemui.statusbar.OperatorNameViewController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
import org.junit.Before;
import org.junit.Ignore;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index e05035d..1de4647 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -170,8 +170,6 @@
private KeyguardBottomAreaView mQsFrame;
private KeyguardStatusView mKeyguardStatusView;
@Mock
- private ViewGroup mBigClockContainer;
- @Mock
private NotificationIconAreaController mNotificationAreaController;
@Mock
private HeadsUpManagerPhone mHeadsUpManager;
@@ -393,7 +391,6 @@
when(mKeyguardBottomArea.getLeftView()).thenReturn(mock(KeyguardAffordanceView.class));
when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class));
when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class));
- when(mView.findViewById(R.id.big_clock_container)).thenReturn(mBigClockContainer);
when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
when(mView.findViewById(R.id.keyguard_status_view))
.thenReturn(mock(KeyguardStatusView.class));
@@ -845,6 +842,18 @@
}
@Test
+ public void testSwitchesToBigClockInSplitShadeOnAod() {
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+
+ mNotificationPanelViewController.setDozing(true, false, null);
+
+ verify(mKeyguardStatusViewController).displayClock(LARGE);
+ }
+
+ @Test
public void testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying() {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 5115614..6e3d5ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -308,18 +308,4 @@
verify(mBouncer).updateKeyguardPosition(1.0f);
}
-
- @Test
- public void testNavBarHiddenWhenSleepAnimationStarts() {
- mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
- assertTrue(mStatusBarKeyguardViewManager.isNavBarVisible());
-
- // Verify that the nav bar is hidden when the screen off animation starts
- doReturn(true).when(mUnlockedScreenOffAnimationController).isScreenOffAnimationPlaying();
- mWakefulnessLifecycle.dispatchFinishedGoingToSleep();
- assertFalse(mStatusBarKeyguardViewManager.isNavBarVisible());
-
- mWakefulnessLifecycle.dispatchFinishedWakingUp();
- assertTrue(mStatusBarKeyguardViewManager.isNavBarVisible());
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 88a3827..6868e18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -77,6 +77,8 @@
import com.android.systemui.InitController;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
@@ -110,6 +112,7 @@
import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -135,7 +138,6 @@
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.tuner.TunerService;
@@ -274,6 +276,8 @@
@Mock private StartingSurface mStartingSurface;
@Mock private OperatorNameViewController mOperatorNameViewController;
@Mock private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
+ @Mock private ActivityLaunchAnimator mActivityLaunchAnimator;
+ @Mock private DialogLaunchAnimator mDialogLaunchAnimator;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -452,7 +456,9 @@
mUnlockedScreenOffAnimationController,
Optional.of(mStartingSurface),
mTunerService,
- mock(DumpManager.class));
+ mock(DumpManager.class),
+ mActivityLaunchAnimator,
+ mDialogLaunchAnimator);
when(mKeyguardViewMediator.registerStatusBar(any(StatusBar.class), any(ViewGroup.class),
any(NotificationPanelViewController.class), any(BiometricUnlockController.class),
any(ViewGroup.class), any(KeyguardBypassController.class)))
@@ -794,6 +800,34 @@
}
@Test
+ public void testSetExpansionAffectsAlpha_onlyWhenHidingKeyguard() {
+ mStatusBar.updateScrimController();
+ verify(mScrimController).setExpansionAffectsAlpha(eq(true));
+
+ clearInvocations(mScrimController);
+ when(mBiometricUnlockController.isBiometricUnlock()).thenReturn(true);
+ mStatusBar.updateScrimController();
+ verify(mScrimController).setExpansionAffectsAlpha(eq(true));
+
+ clearInvocations(mScrimController);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ mStatusBar.updateScrimController();
+ verify(mScrimController).setExpansionAffectsAlpha(eq(false));
+
+ clearInvocations(mScrimController);
+ reset(mKeyguardStateController);
+ when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
+ mStatusBar.updateScrimController();
+ verify(mScrimController).setExpansionAffectsAlpha(eq(false));
+
+ clearInvocations(mScrimController);
+ reset(mKeyguardStateController);
+ when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true);
+ mStatusBar.updateScrimController();
+ verify(mScrimController).setExpansionAffectsAlpha(eq(false));
+ }
+
+ @Test
public void testTransitionLaunch_noPreview_doesntGoUnlocked() {
mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
mStatusBar.showKeyguardImpl();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index efe2c17..5b6c244 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -33,6 +33,7 @@
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -105,7 +106,9 @@
mockActivityStarter,
mainExecutor,
mockIActivityManager,
- OngoingCallLogger(uiEventLoggerFake))
+ OngoingCallLogger(uiEventLoggerFake),
+ DumpManager(),
+ )
controller.init()
controller.addCallback(mockOngoingCallListener)
controller.setChipView(chipView)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
index a59ba8c..a846b06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy
+import android.app.IActivityManager
import android.app.IActivityTaskManager
import android.app.admin.DevicePolicyManager
import android.content.Context
@@ -31,6 +32,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
+import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.util.UserIcons
import com.android.systemui.GuestResumeSessionReceiver
@@ -54,10 +56,11 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.anyString
import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -65,6 +68,7 @@
@SmallTest
class UserSwitcherControllerTest : SysuiTestCase() {
@Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var activityManager: IActivityManager
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var handler: Handler
@@ -78,6 +82,7 @@
@Mock private lateinit var secureSettings: SecureSettings
@Mock private lateinit var falsingManager: FalsingManager
@Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var interactionJankMonitor: InteractionJankMonitor
private lateinit var testableLooper: TestableLooper
private lateinit var uiBgExecutor: FakeExecutor
private lateinit var uiEventLogger: UiEventLoggerFake
@@ -110,6 +115,7 @@
userSwitcherController = UserSwitcherController(
context,
+ activityManager,
userManager,
userTracker,
keyguardStateController,
@@ -125,6 +131,7 @@
userDetailAdapter,
secureSettings,
uiBgExecutor,
+ interactionJankMonitor,
dumpManager)
userSwitcherController.mPauseRefreshUsers = true
@@ -132,7 +139,7 @@
}
@Test
- fun testAddGuest_okButtonPressed_isLogged() {
+ fun testAddGuest_okButtonPressed() {
val emptyGuestUserRecord = UserSwitcherController.UserRecord(
null,
null,
@@ -148,6 +155,8 @@
userSwitcherController.onUserListItemClicked(emptyGuestUserRecord)
testableLooper.processAllMessages()
+ verify(interactionJankMonitor).begin(any())
+ verify(activityManager).switchUser(guestInfo.id)
assertEquals(1, uiEventLogger.numLogs())
assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_ADD.id, uiEventLogger.eventId(0))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
index de86821..b54aadb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
@@ -138,7 +138,7 @@
private fun createWallpaperInfo(useDefaultTransition: Boolean = true): WallpaperInfo {
val info = mock(WallpaperInfo::class.java)
- whenever(info.shouldUseDefaultDeviceStateChangeTransition())
+ whenever(info.shouldUseDefaultDisplayStateChangeTransition())
.thenReturn(useDefaultTransition)
return info
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
index e7acfae..8ea9da6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
@@ -18,9 +18,9 @@
import android.testing.LeakCheck;
import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.statusbar.connectivity.NetworkController;
+import com.android.systemui.statusbar.connectivity.NetworkController.SignalCallback;
import com.android.systemui.statusbar.policy.DataSaverController;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
public class FakeNetworkController extends BaseLeakChecker<SignalCallback>
implements NetworkController {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
index fedc08d..dc6a8fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
@@ -19,6 +19,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -29,7 +30,6 @@
import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.NetworkController;
import com.android.systemui.statusbar.policy.NextAlarmController;
import com.android.systemui.statusbar.policy.RotationLockController;
import com.android.systemui.statusbar.policy.SecurityController;
diff --git a/proto/src/critical_event_log.proto b/proto/src/critical_event_log.proto
new file mode 100644
index 0000000..cb05a71
--- /dev/null
+++ b/proto/src/critical_event_log.proto
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package com.android.server.am;
+
+option java_multiple_files = true;
+
+// Output proto containing recent critical events for inclusion in logs such as ANR files.
+// Do not change the field names since this data is dumped to ANR files in textproto format.
+message CriticalEventLogProto {
+ // Timestamp when the log snapshot was generated.
+ // Required.
+ optional int64 timestamp_ms = 1;
+
+ // Max age of events that are included in this proto.
+ // Required.
+ optional int32 window_ms = 2;
+
+ // Max number of events in the log.
+ // Note: if the number of events is equal to the capacity then it is likely the actual time range
+ // covered by the log is shorter than window_ms.
+ // Required.
+ optional int32 capacity = 3;
+
+ // Recent critical events.
+ repeated CriticalEventProto events = 4;
+}
+
+// On-disk storage of events.
+message CriticalEventLogStorageProto {
+ repeated CriticalEventProto events = 1;
+}
+
+// A "critical" event such as an ANR or watchdog.
+// Do not change the field names since this data is dumped to ANR files in textproto format.
+message CriticalEventProto {
+ // Timestamp of the event.
+ // Required.
+ optional int64 timestamp_ms = 1;
+
+ // Required.
+ oneof event {
+ Watchdog watchdog = 2;
+ HalfWatchdog half_watchdog = 3;
+ }
+
+ message Watchdog {
+ // The watchdog subject.
+ // Required.
+ optional string subject = 1;
+
+ // Unique identifier of the watchdog.
+ // Can be used to join with other data for this watchdog such as stack dumps & perfetto traces.
+ // Generated in {@link com.android.server.Watchdog#run}.
+ // Required.
+ optional string uuid = 2;
+ }
+
+ message HalfWatchdog {
+ // The half-watchdog subject.
+ // Required.
+ optional string subject = 1;
+ }
+}
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 718da9e..8f30aa9a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -353,7 +353,7 @@
mMagnificationActivated = spec.scale > 1.0f;
if (mMagnificationActivated != lastMagnificationActivated) {
mMagnificationInfoChangedCallback.onFullScreenMagnificationActivationState(
- mMagnificationActivated);
+ mDisplayId, mMagnificationActivated);
}
}
@@ -1614,9 +1614,10 @@
* Called when the state of the magnification activation is changed.
* It is for the logging data of the magnification activation state.
*
+ * @param displayId The logical display id.
* @param activated {@code true} if the magnification is activated, otherwise {@code false}.
*/
- void onFullScreenMagnificationActivationState(boolean activated);
+ void onFullScreenMagnificationActivationState(int displayId, boolean activated);
/**
* Called when the IME window visibility changed.
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index c3d8d4c..8f4a5cb 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -869,8 +869,6 @@
mPromptController.showNotificationIfNeeded();
zoomOn(up.getX(), up.getY());
}
-
- mCallback.onTripleTapped(mDisplayId, getMode());
}
private boolean isMagnifying() {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 19601b4..5a6836c 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -48,11 +48,12 @@
* the user touch interaction starts if magnification capabilities is all. </li>
* <li> 2. {@link #onTouchInteractionEnd} shows magnification switch UI when
* the user touch interaction ends if magnification capabilities is all. </li>
- * <li> 3. {@link #onShortcutTriggered} updates magnification switch UI depending on
- * magnification capabilities and magnification active state when magnification shortcut
- * is triggered.</li>
- * <li> 4. {@link #onTripleTapped} updates magnification switch UI depending on magnification
- * capabilities and magnification active state when triple-tap gesture is detected. </li>
+ * <li> 3. {@link #onWindowMagnificationActivationState} updates magnification switch UI
+ * depending on magnification capabilities and magnification active state when window
+ * magnification activation state change.</li>
+ * <li> 4. {@link #onFullScreenMagnificationActivationState} updates magnification switch UI
+ * depending on magnification capabilities and magnification active state when fullscreen
+ * magnification activation state change.</li>
* <li> 4. {@link #onRequestMagnificationSpec} updates magnification switch UI depending on
* magnification capabilities and magnification active state when new magnification spec is
* changed by external request from calling public APIs. </li>
@@ -142,16 +143,6 @@
}
}
- @Override
- public void onShortcutTriggered(int displayId, int mode) {
- updateMagnificationButton(displayId, mode);
- }
-
- @Override
- public void onTripleTapped(int displayId, int mode) {
- updateMagnificationButton(displayId, mode);
- }
-
private void updateMagnificationButton(int displayId, int mode) {
final boolean isActivated = isActivated(displayId, mode);
final boolean showButton;
@@ -250,6 +241,7 @@
mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
}
}
+ updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
}
private void disableFullScreenMagnificationIfNeeded(int displayId) {
@@ -264,7 +256,7 @@
}
@Override
- public void onFullScreenMagnificationActivationState(boolean activated) {
+ public void onFullScreenMagnificationActivationState(int displayId, boolean activated) {
if (activated) {
mFullScreenModeEnabledTime = SystemClock.uptimeMillis();
@@ -280,6 +272,7 @@
mActivatedMode = ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
}
}
+ updateMagnificationButton(displayId, ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
}
@Override
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index 19b3396..2894693 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -80,25 +80,6 @@
* @param mode The magnification mode
*/
void onTouchInteractionEnd(int displayId, int mode);
-
- /**
- * Called when the magnification shortcut is triggered by a user. The magnification
- * shortcut can be accessibility button or volume shortcut.
- *
- * @param displayId The logical display id
- * @param mode The magnification mode
- */
- void onShortcutTriggered(int displayId, int mode);
-
- /**
- * Called when the triple-tap gesture is handled. The magnification
- * shortcut can be a triple-tap gesture or accessibility button.
- * Called when the triple-tap gesture is handled
- *
- * @param displayId The logical display id
- * @param mode The magnification mode
- */
- void onTripleTapped(int displayId, int mode);
}
private final AccessibilityTraceManager mTrace;
@@ -192,7 +173,6 @@
}
if (mDetectShortcutTrigger) {
handleShortcutTriggered();
- mCallback.onShortcutTriggered(mDisplayId, getMode());
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index b26d364..bc61284 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -202,7 +202,6 @@
Slog.i(mLogTag, "onTripleTap()");
}
toggleMagnification(up.getX(), up.getY());
- mCallback.onTripleTapped(mDisplayId, getMode());
}
void resetToDetectState() {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index d911388..f62935a 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1154,6 +1154,11 @@
public boolean disable(AttributionSource attributionSource, boolean persist)
throws RemoteException {
+ if (!persist) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
+ "Need BLUETOOTH_PRIVILEGED permission");
+ }
+
final String packageName = attributionSource.getPackageName();
if (!checkBluetoothPermissions(attributionSource, "disable", true)) {
if (DBG) {
@@ -2599,7 +2604,7 @@
intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null,
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL, null,
getTempAllowlistBroadcastOptions());
}
}
diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java
index ba0266e..57ce342 100644
--- a/services/core/java/com/android/server/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/SensorPrivacyService.java
@@ -711,6 +711,16 @@
@Override
public void setIndividualSensorPrivacy(@UserIdInt int userId,
@SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable) {
+ if (DEBUG) {
+ Log.d(TAG, "callingUid=" + Binder.getCallingUid()
+ + " callingPid=" + Binder.getCallingPid()
+ + " setIndividualSensorPrivacy("
+ + "userId=" + userId
+ + " source=" + source
+ + " sensor=" + sensor
+ + " enable=" + enable
+ + ")");
+ }
enforceManageSensorPrivacyPermission();
if (userId == UserHandle.USER_CURRENT) {
userId = mCurrentUser;
@@ -895,6 +905,14 @@
@Override
public boolean isIndividualSensorPrivacyEnabled(@UserIdInt int userId, int sensor) {
+ if (DEBUG) {
+ Log.d(TAG, "callingUid=" + Binder.getCallingUid()
+ + " callingPid=" + Binder.getCallingPid()
+ + " isIndividualSensorPrivacyEnabled("
+ + "userId=" + userId
+ + " sensor=" + sensor
+ + ")");
+ }
enforceObserveSensorPrivacyPermission();
if (userId == UserHandle.USER_CURRENT) {
userId = mCurrentUser;
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 3a6d9a2..311555e 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1544,7 +1544,18 @@
}
if (vol.type == VolumeInfo.TYPE_EMULATED) {
- if (!mStorageSessionController.supportsExternalStorage(vol.mountUserId)) {
+ final Context volumeUserContext = mContext.createContextAsUser(
+ UserHandle.of(vol.mountUserId), 0);
+
+ boolean isMediaSharedWithParent =
+ (volumeUserContext != null) ? volumeUserContext.getSystemService(
+ UserManager.class).isMediaSharedWithParent() : false;
+
+ // For all the users where media is shared with parent, creation of emulated volume
+ // should not be skipped even if media provider instance is not running in that user
+ // space
+ if (!isMediaSharedWithParent
+ && !mStorageSessionController.supportsExternalStorage(vol.mountUserId)) {
Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user "
+ Integer.toString(vol.mountUserId)
+ " does not support external storage.");
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 364e733..53b6605 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -192,7 +192,7 @@
final ArrayList<Integer> pids = new ArrayList<>();
pids.add(Process.myPid());
ActivityManagerService.dumpStackTraces(pids, null, null,
- Watchdog.getInterestingNativePids(), null, null);
+ Watchdog.getInterestingNativePids(), null, null, null);
}
@Override
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index b4413a4..63301ac 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1025,7 +1025,6 @@
return;
}
- int phoneId = getPhoneIdFromSubId(subId);
synchronized (mRecords) {
// register
IBinder b = callback.asBinder();
@@ -1048,21 +1047,24 @@
// Legacy applications pass SubscriptionManager.DEFAULT_SUB_ID,
// force all illegal subId to SubscriptionManager.DEFAULT_SUB_ID
if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ if (DBG) {
+ log("invalid subscription id, use default id");
+ }
r.subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
} else {//APP specify subID
r.subId = subId;
}
- r.phoneId = phoneId;
+ r.phoneId = getPhoneIdFromSubId(r.subId);
r.eventList = events;
if (DBG) {
- log("listen: Register r=" + r + " r.subId=" + r.subId + " phoneId=" + phoneId);
+ log("listen: Register r=" + r + " r.subId=" + r.subId + " r.phoneId=" + r.phoneId);
}
- if (notifyNow && validatePhoneId(phoneId)) {
+ if (notifyNow && validatePhoneId(r.phoneId)) {
if (events.contains(TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)){
try {
- if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
- ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
+ if (VDBG) log("listen: call onSSC state=" + mServiceState[r.phoneId]);
+ ServiceState rawSs = new ServiceState(mServiceState[r.phoneId]);
if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
r.callback.onServiceStateChanged(rawSs);
} else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
@@ -1078,8 +1080,8 @@
}
if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)) {
try {
- if (mSignalStrength[phoneId] != null) {
- int gsmSignalStrength = mSignalStrength[phoneId]
+ if (mSignalStrength[r.phoneId] != null) {
+ int gsmSignalStrength = mSignalStrength[r.phoneId]
.getGsmSignalStrength();
r.callback.onSignalStrengthChanged((gsmSignalStrength == 99 ? -1
: gsmSignalStrength));
@@ -1092,7 +1094,7 @@
TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)) {
try {
r.callback.onMessageWaitingIndicatorChanged(
- mMessageWaiting[phoneId]);
+ mMessageWaiting[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1101,7 +1103,7 @@
TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)) {
try {
r.callback.onCallForwardingIndicatorChanged(
- mCallForwarding[phoneId]);
+ mCallForwarding[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1109,11 +1111,11 @@
if (validateEventAndUserLocked(
r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)) {
try {
- if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[phoneId]);
+ if (DBG_LOC) log("listen: mCellIdentity = " + mCellIdentity[r.phoneId]);
if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
&& checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
// null will be translated to empty CellLocation object in client.
- r.callback.onCellLocationChanged(mCellIdentity[phoneId]);
+ r.callback.onCellLocationChanged(mCellIdentity[r.phoneId]);
}
} catch (RemoteException ex) {
remove(r.binder);
@@ -1121,38 +1123,38 @@
}
if (events.contains(TelephonyCallback.EVENT_LEGACY_CALL_STATE_CHANGED)) {
try {
- r.callback.onLegacyCallStateChanged(mCallState[phoneId],
- getCallIncomingNumber(r, phoneId));
+ r.callback.onLegacyCallStateChanged(mCallState[r.phoneId],
+ getCallIncomingNumber(r, r.phoneId));
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_CALL_STATE_CHANGED)) {
try {
- r.callback.onCallStateChanged(mCallState[phoneId]);
+ r.callback.onCallStateChanged(mCallState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)) {
try {
- r.callback.onDataConnectionStateChanged(mDataConnectionState[phoneId],
- mDataConnectionNetworkType[phoneId]);
+ r.callback.onDataConnectionStateChanged(mDataConnectionState[r.phoneId],
+ mDataConnectionNetworkType[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)) {
try {
- r.callback.onDataActivity(mDataActivity[phoneId]);
+ r.callback.onDataActivity(mDataActivity[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)) {
try {
- if (mSignalStrength[phoneId] != null) {
- r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+ if (mSignalStrength[r.phoneId] != null) {
+ r.callback.onSignalStrengthsChanged(mSignalStrength[r.phoneId]);
}
} catch (RemoteException ex) {
remove(r.binder);
@@ -1162,8 +1164,8 @@
TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED)) {
updateReportSignalStrengthDecision(r.subId);
try {
- if (mSignalStrength[phoneId] != null) {
- r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+ if (mSignalStrength[r.phoneId] != null) {
+ r.callback.onSignalStrengthsChanged(mSignalStrength[r.phoneId]);
}
} catch (RemoteException ex) {
remove(r.binder);
@@ -1172,11 +1174,13 @@
if (validateEventAndUserLocked(
r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)) {
try {
- if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
- + mCellInfo.get(phoneId));
+ if (DBG_LOC) {
+ log("listen: mCellInfo[" + r.phoneId + "] = "
+ + mCellInfo.get(r.phoneId));
+ }
if (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
&& checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
- r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
+ r.callback.onCellInfoChanged(mCellInfo.get(r.phoneId));
}
} catch (RemoteException ex) {
remove(r.binder);
@@ -1184,22 +1188,22 @@
}
if (events.contains(TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)) {
try {
- r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
+ r.callback.onPreciseCallStateChanged(mPreciseCallState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)) {
try {
- r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
- mCallPreciseDisconnectCause[phoneId]);
+ r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[r.phoneId],
+ mCallPreciseDisconnectCause[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)) {
try {
- r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(phoneId));
+ r.callback.onImsCallDisconnectCauseChanged(mImsReasonInfo.get(r.phoneId));
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1208,7 +1212,7 @@
TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)) {
try {
for (PreciseDataConnectionState pdcs
- : mPreciseDataConnectionStates.get(phoneId).values()) {
+ : mPreciseDataConnectionStates.get(r.phoneId).values()) {
r.callback.onPreciseDataConnectionStateChanged(pdcs);
}
} catch (RemoteException ex) {
@@ -1225,29 +1229,29 @@
if (events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)) {
try {
r.callback.onVoiceActivationStateChanged(
- mVoiceActivationState[phoneId]);
+ mVoiceActivationState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED)) {
try {
- r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]);
+ r.callback.onDataActivationStateChanged(mDataActivationState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)) {
try {
- r.callback.onUserMobileDataStateChanged(mUserMobileDataState[phoneId]);
+ r.callback.onUserMobileDataStateChanged(mUserMobileDataState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)) {
try {
- if (mTelephonyDisplayInfos[phoneId] != null) {
- r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[phoneId]);
+ if (mTelephonyDisplayInfos[r.phoneId] != null) {
+ r.callback.onDisplayInfoChanged(mTelephonyDisplayInfos[r.phoneId]);
}
} catch (RemoteException ex) {
remove(r.binder);
@@ -1284,20 +1288,20 @@
}
if (events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)) {
try {
- r.callback.onSrvccStateChanged(mSrvccState[phoneId]);
+ r.callback.onSrvccStateChanged(mSrvccState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)) {
try {
- r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
+ r.callback.onCallAttributesChanged(mCallAttributes[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
}
if (events.contains(TelephonyCallback.EVENT_BARRING_INFO_CHANGED)) {
- BarringInfo barringInfo = mBarringInfo.get(phoneId);
+ BarringInfo barringInfo = mBarringInfo.get(r.phoneId);
BarringInfo biNoLocation = barringInfo != null
? barringInfo.createLocationInfoSanitizedCopy() : null;
if (VDBG) log("listen: call onBarringInfoChanged=" + barringInfo);
@@ -1315,8 +1319,8 @@
r.callback.onPhysicalChannelConfigChanged(
shouldSanitizeLocationForPhysicalChannelConfig(r)
? getLocationSanitizedConfigs(
- mPhysicalChannelConfigs.get(phoneId))
- : mPhysicalChannelConfigs.get(phoneId));
+ mPhysicalChannelConfigs.get(r.phoneId))
+ : mPhysicalChannelConfigs.get(r.phoneId));
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1325,7 +1329,7 @@
TelephonyCallback.EVENT_DATA_ENABLED_CHANGED)) {
try {
r.callback.onDataEnabledChanged(
- mIsDataEnabled[phoneId], mDataEnabledReason[phoneId]);
+ mIsDataEnabled[r.phoneId], mDataEnabledReason[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1333,9 +1337,9 @@
if (events.contains(
TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED)) {
try {
- if (mLinkCapacityEstimateLists.get(phoneId) != null) {
+ if (mLinkCapacityEstimateLists.get(r.phoneId) != null) {
r.callback.onLinkCapacityEstimateChanged(mLinkCapacityEstimateLists
- .get(phoneId));
+ .get(r.phoneId));
}
} catch (RemoteException ex) {
remove(r.binder);
@@ -1577,7 +1581,7 @@
}
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_SERVICE_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
ServiceState stateToSend;
@@ -1639,7 +1643,7 @@
if ((activationType == SIM_ACTIVATION_TYPE_VOICE)
&& r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
if (DBG) {
log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r
+ " subId=" + subId + " phoneId=" + phoneId
@@ -1650,7 +1654,7 @@
if ((activationType == SIM_ACTIVATION_TYPE_DATA)
&& r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_DATA_ACTIVATION_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
if (DBG) {
log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r
+ " subId=" + subId + " phoneId=" + phoneId
@@ -1692,7 +1696,7 @@
TelephonyCallback.EVENT_SIGNAL_STRENGTHS_CHANGED)
|| r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_ALWAYS_REPORTED_SIGNAL_STRENGTH_CHANGED))
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (DBG) {
log("notifySignalStrengthForPhoneId: callback.onSsS r=" + r
@@ -1706,7 +1710,7 @@
}
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_SIGNAL_STRENGTH_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
int gsmSignalStrength = signalStrength.getGsmSignalStrength();
int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
@@ -1753,7 +1757,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onCarrierNetworkChange(active);
} catch (RemoteException ex) {
@@ -1785,7 +1789,7 @@
for (Record r : mRecords) {
if (validateEventAndUserLocked(
r, TelephonyCallback.EVENT_CELL_INFO_CHANGED)
- && idMatch(r.subId, subId, phoneId)
+ && idMatch(r, subId, phoneId)
&& (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
&& checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
try {
@@ -1819,7 +1823,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_MESSAGE_WAITING_INDICATOR_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onMessageWaitingIndicatorChanged(mwi);
} catch (RemoteException ex) {
@@ -1846,7 +1850,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_USER_MOBILE_DATA_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onUserMobileDataStateChanged(state);
} catch (RemoteException ex) {
@@ -1885,7 +1889,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_DISPLAY_INFO_CHANGED)
- && idMatchWithoutDefaultPhoneCheck(r.subId, subId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (!mConfigurationProvider.isDisplayInfoNrAdvancedSupported(
r.callingPackage, Binder.getCallingUserHandle())) {
@@ -1937,7 +1941,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CALL_FORWARDING_INDICATOR_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onCallForwardingIndicatorChanged(cfi);
} catch (RemoteException ex) {
@@ -1966,7 +1970,7 @@
// Notify by correct subId.
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_DATA_ACTIVITY_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onDataActivity(state);
} catch (RemoteException ex) {
@@ -2014,7 +2018,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_DATA_CONNECTION_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (DBG) {
log("Notify data connection state changed on sub: " + subId);
@@ -2039,7 +2043,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_PRECISE_DATA_CONNECTION_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onPreciseDataConnectionStateChanged(preciseState);
} catch (RemoteException ex) {
@@ -2086,7 +2090,7 @@
for (Record r : mRecords) {
if (validateEventAndUserLocked(
r, TelephonyCallback.EVENT_CELL_LOCATION_CHANGED)
- && idMatch(r.subId, subId, phoneId)
+ && idMatch(r, subId, phoneId)
&& (checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE)
&& checkFineLocationAccess(r, Build.VERSION_CODES.Q))) {
try {
@@ -2140,7 +2144,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_PRECISE_CALL_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onPreciseCallStateChanged(mPreciseCallState[phoneId]);
} catch (RemoteException ex) {
@@ -2149,7 +2153,7 @@
}
if (notifyCallAttributes && r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
} catch (RemoteException ex) {
@@ -2174,7 +2178,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CALL_DISCONNECT_CAUSE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onCallDisconnectCauseChanged(mCallDisconnectCause[phoneId],
mCallPreciseDisconnectCause[phoneId]);
@@ -2199,7 +2203,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_IMS_CALL_DISCONNECT_CAUSE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (DBG_LOC) {
log("notifyImsCallDisconnectCause: mImsReasonInfo="
@@ -2231,7 +2235,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_SRVCC_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (DBG_LOC) {
log("notifySrvccStateChanged: mSrvccState=" + state + " r=" + r);
@@ -2260,7 +2264,7 @@
}
if ((r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_OEM_HOOK_RAW))
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onOemHookRawEvent(rawData);
} catch (RemoteException ex) {
@@ -2340,7 +2344,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onRadioPowerStateChanged(state);
} catch (RemoteException ex) {
@@ -2369,7 +2373,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_EMERGENCY_NUMBER_LIST_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
if (VDBG) {
@@ -2456,7 +2460,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_CALL_ATTRIBUTES_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onCallAttributesChanged(mCallAttributes[phoneId]);
} catch (RemoteException ex) {
@@ -2487,7 +2491,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_REGISTRATION_FAILURE)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onRegistrationFailed(
checkFineLocationAccess(r, Build.VERSION_CODES.BASE)
@@ -2530,7 +2534,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_BARRING_INFO_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (DBG_LOC) {
log("notifyBarringInfo: mBarringInfo="
@@ -2575,7 +2579,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_PHYSICAL_CHANNEL_CONFIG_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (DBG_LOC) {
log("notifyPhysicalChannelConfig: mPhysicalChannelConfigs="
@@ -2642,7 +2646,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_DATA_ENABLED_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onDataEnabledChanged(enabled, reason);
} catch (RemoteException ex) {
@@ -2677,7 +2681,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
if (VDBG) {
log("notifyAllowedNetworkTypesChanged: reason= " + reason
@@ -2719,7 +2723,7 @@
for (Record r : mRecords) {
if (r.matchTelephonyCallbackEvent(
TelephonyCallback.EVENT_LINK_CAPACITY_ESTIMATE_CHANGED)
- && idMatch(r.subId, subId, phoneId)) {
+ && idMatch(r, subId, phoneId)) {
try {
r.callback.onLinkCapacityEstimateChanged(linkCapacityEstimateList);
} catch (RemoteException ex) {
@@ -3169,33 +3173,24 @@
}
/**
- * If the registrant specified a subId, then we should only notify it if subIds match.
- * If the registrant registered with DEFAULT subId, we should notify only when the related subId
- * is default subId (which could be INVALID if there's no default subId).
+ * Match the sub id or phone id of the event to the record
*
- * This should be the correct way to check record ID match. in idMatch the record's phoneId is
- * speculated based on subId passed by the registrant so it's not a good reference.
- * But to avoid triggering potential regression only replace idMatch with it when an issue with
- * idMatch is reported. Eventually this should replace all instances of idMatch.
+ * We follow the rules below:
+ * 1) If sub id of the event is invalid, phone id should be used.
+ * 2) The event on default sub should be notified to the records
+ * which register the default sub id.
+ * 3) Sub id should be exactly matched for all other cases.
*/
- private boolean idMatchWithoutDefaultPhoneCheck(int subIdInRecord, int subIdToNotify) {
- if (subIdInRecord == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
- return (subIdToNotify == mDefaultSubId);
- } else {
- return (subIdInRecord == subIdToNotify);
- }
- }
+ boolean idMatch(Record r, int subId, int phoneId) {
- boolean idMatch(int rSubId, int subId, int phoneId) {
-
- if(subId < 0) {
- // Invalid case, we need compare phoneId with default one.
- return (mDefaultPhoneId == phoneId);
+ if (subId < 0) {
+ // Invalid case, we need compare phoneId.
+ return (r.phoneId == phoneId);
}
- if(rSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ if (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
return (subId == mDefaultSubId);
} else {
- return (rSubId == subId);
+ return (r.subId == subId);
}
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index fcd049f..46efa3c 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -45,6 +45,7 @@
import com.android.internal.os.ZygoteConnectionConstants;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.am.ActivityManagerService;
+import com.android.server.am.CriticalEventLog;
import com.android.server.am.TraceErrorLogger;
import com.android.server.wm.SurfaceAnimationThread;
@@ -661,10 +662,15 @@
} // END synchronized (mLock)
if (doWaitedHalfDump) {
+ // Get critical event log before logging the half watchdog so that it doesn't
+ // occur in the log.
+ String criticalEvents = CriticalEventLog.getInstance().logLinesForAnrFile();
+ CriticalEventLog.getInstance().logHalfWatchdog(subject);
+
// We've waited half the deadlock-detection interval. Pull a stack
// trace and wait another half.
ActivityManagerService.dumpStackTraces(pids, null, null,
- getInterestingNativePids(), null, subject);
+ getInterestingNativePids(), null, subject, criticalEvents);
continue;
}
@@ -673,12 +679,9 @@
// Then kill this process so that the system will restart.
EventLog.writeEvent(EventLogTags.WATCHDOG, subject);
- final UUID errorId;
+ final UUID errorId = mTraceErrorLogger.generateErrorId();
if (mTraceErrorLogger.isAddErrorIdEnabled()) {
- errorId = mTraceErrorLogger.generateErrorId();
mTraceErrorLogger.addErrorIdToTrace("system_server", errorId);
- } else {
- errorId = null;
}
// Log the atom as early as possible since it is used as a mechanism to trigger
@@ -686,6 +689,11 @@
// point in time when the Watchdog happens as possible.
FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject);
+ // Get critical event log before logging the watchdog so that it doesn't occur in the
+ // log.
+ String criticalEvents = CriticalEventLog.getInstance().logLinesForAnrFile();
+ CriticalEventLog.getInstance().logWatchdog(subject, errorId);
+
long anrTime = SystemClock.uptimeMillis();
StringBuilder report = new StringBuilder();
report.append(MemoryPressureUtil.currentPsiState());
@@ -693,7 +701,7 @@
StringWriter tracesFileException = new StringWriter();
final File stack = ActivityManagerService.dumpStackTraces(
pids, processCpuTracker, new SparseArray<>(), getInterestingNativePids(),
- tracesFileException, subject);
+ tracesFileException, subject, criticalEvents);
// Give some extra time to make sure the stack traces get written.
// The system's been hanging for a minute, another second or two won't hurt much.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d44f5e8..6a63254 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1569,6 +1569,13 @@
private static final int INDEX_TOTAL_MEMTRACK_GL = 14;
private static final int INDEX_LAST = 15;
+ /**
+ * Used to notify activity lifecycle events.
+ */
+ @Nullable
+ volatile ActivityManagerInternal.VoiceInteractionManagerProvider
+ mVoiceInteractionManagerProvider;
+
final class UiHandler extends Handler {
public UiHandler() {
super(com.android.server.UiThread.get().getLooper(), null, true);
@@ -1905,6 +1912,14 @@
return mAppOpsService;
}
+ /**
+ * Sets the internal voice interaction manager service.
+ */
+ private void setVoiceInteractionManagerProvider(
+ @Nullable ActivityManagerInternal.VoiceInteractionManagerProvider provider) {
+ mVoiceInteractionManagerProvider = provider;
+ }
+
static class MemBinder extends Binder {
ActivityManagerService mActivityManagerService;
private final PriorityDump.PriorityDumper mPriorityDumper =
@@ -2379,6 +2394,7 @@
private void start() {
removeAllProcessGroups();
+ CriticalEventLog.init();
mBatteryStatsService.publish();
mAppOpsService.publish();
Slog.d("AppOps", "AppOpsService published");
@@ -2758,6 +2774,11 @@
|| event == Event.ACTIVITY_DESTROYED)) {
contentCaptureService.notifyActivityEvent(userId, activity, event);
}
+ // TODO(b/201234353): Move the logic to client side.
+ if (mVoiceInteractionManagerProvider != null && (event == Event.ACTIVITY_PAUSED
+ || event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED)) {
+ mVoiceInteractionManagerProvider.notifyActivityEventChanged();
+ }
}
/**
@@ -3179,7 +3200,7 @@
ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile) {
return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePids,
- logExceptionCreatingFile, null, null);
+ logExceptionCreatingFile, null, null, null);
}
/**
@@ -3189,13 +3210,14 @@
* @param nativePids optional list of native pids to dump stack crawls
* @param logExceptionCreatingFile optional writer to which we log errors creating the file
* @param subject optional line related to the error
+ * @param criticalEventSection optional lines containing recent critical events.
*/
public static File dumpStackTraces(ArrayList<Integer> firstPids,
ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile,
- String subject) {
+ String subject, String criticalEventSection) {
return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePids,
- logExceptionCreatingFile, null, subject);
+ logExceptionCreatingFile, null, subject, criticalEventSection);
}
/**
@@ -3205,7 +3227,7 @@
/* package */ static File dumpStackTraces(ArrayList<Integer> firstPids,
ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile,
- long[] firstPidOffsets, String subject) {
+ long[] firstPidOffsets, String subject, String criticalEventSection) {
ArrayList<Integer> extraPids = null;
Slog.i(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids);
@@ -3257,12 +3279,17 @@
return null;
}
- if (subject != null) {
+ if (subject != null || criticalEventSection != null) {
try (FileOutputStream fos = new FileOutputStream(tracesFile, true)) {
- String header = "Subject: " + subject + "\n";
- fos.write(header.getBytes(StandardCharsets.UTF_8));
+ if (subject != null) {
+ String header = "Subject: " + subject + "\n\n";
+ fos.write(header.getBytes(StandardCharsets.UTF_8));
+ }
+ if (criticalEventSection != null) {
+ fos.write(criticalEventSection.getBytes(StandardCharsets.UTF_8));
+ }
} catch (IOException e) {
- Slog.w(TAG, "Exception writing subject to ANR dump file:", e);
+ Slog.w(TAG, "Exception writing to ANR dump file:", e);
}
}
@@ -11810,8 +11837,8 @@
restart = app.onCleanupApplicationRecordLSP(mProcessStats, allowRestart,
fromBinderDied || app.isolated /* unlinkDeath */);
- // Cancel pending frozen task if there is any.
- mOomAdjuster.mCachedAppOptimizer.unscheduleFreezeAppLSP(app);
+ // Cancel pending frozen task and clean up frozen record if there is any.
+ mOomAdjuster.mCachedAppOptimizer.onCleanupApplicationRecordLocked(app);
}
mAppProfiler.onCleanupApplicationRecordLocked(app);
skipCurrentReceiverLocked(app);
@@ -12558,11 +12585,14 @@
}
// If the change is enabled, but neither exported or not exported is set, we need to log
- // an error so the consumer can know to explicitly set the value for their flag
- if (!onlyProtectedBroadcasts && (Compatibility.isChangeEnabled(
- DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED)
- && (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED))
- == 0)) {
+ // an error so the consumer can know to explicitly set the value for their flag.
+ // If the caller is registering for a sticky broadcast with a null receiver, we won't
+ // require a flag
+ if (!onlyProtectedBroadcasts && receiver != null && (
+ Compatibility.isChangeEnabled(
+ DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED)
+ && (flags & (Context.RECEIVER_EXPORTED | Context.RECEIVER_NOT_EXPORTED))
+ == 0)) {
Slog.e(TAG,
callerPackage + ": Targeting T+ (version " + Build.VERSION_CODES.TIRAMISU
+ " and above) requires that one of RECEIVER_EXPORTED or "
@@ -16573,6 +16603,12 @@
return ActivityManagerService.this.sendIntentSender(target, allowlistToken, code,
intent, resolvedType, finishedReceiver, requiredPermission, options);
}
+
+ @Override
+ public void setVoiceInteractionManagerProvider(
+ @Nullable ActivityManagerInternal.VoiceInteractionManagerProvider provider) {
+ ActivityManagerService.this.setVoiceInteractionManagerProvider(provider);
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 6230919..0c94fbb 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -38,6 +38,7 @@
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Slog;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -173,6 +174,10 @@
private final ArrayList<ProcessRecord> mPendingCompactionProcesses =
new ArrayList<ProcessRecord>();
+ @GuardedBy("mProcLock")
+ private final SparseArray<ProcessRecord> mFrozenProcesses =
+ new SparseArray<>();
+
private final ActivityManagerService mAm;
private final ActivityManagerGlobalLock mProcLock;
@@ -191,7 +196,9 @@
} else if (KEY_COMPACT_THROTTLE_1.equals(name)
|| KEY_COMPACT_THROTTLE_2.equals(name)
|| KEY_COMPACT_THROTTLE_3.equals(name)
- || KEY_COMPACT_THROTTLE_4.equals(name)) {
+ || KEY_COMPACT_THROTTLE_4.equals(name)
+ || KEY_COMPACT_THROTTLE_5.equals(name)
+ || KEY_COMPACT_THROTTLE_6.equals(name)) {
updateCompactionThrottles();
} else if (KEY_COMPACT_STATSD_SAMPLE_RATE.equals(name)) {
updateCompactStatsdSampleRate();
@@ -422,6 +429,15 @@
pw.println(" " + KEY_USE_FREEZER + "=" + mUseFreezer);
pw.println(" " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate);
pw.println(" " + KEY_FREEZER_DEBOUNCE_TIMEOUT + "=" + mFreezerDebounceTimeout);
+ synchronized (mProcLock) {
+ int size = mFrozenProcesses.size();
+ pw.println(" Apps frozen: " + size);
+ for (int i = 0; i < size; i++) {
+ ProcessRecord app = mFrozenProcesses.valueAt(i);
+ pw.println(" " + app.mOptRecord.getFreezeUnfreezeTime()
+ + ": " + app.getPid() + " " + app.processName);
+ }
+ }
if (DEBUG_COMPACTION) {
for (Map.Entry<Integer, LastCompactionStats> entry
: mLastCompactionStats.entrySet()) {
@@ -999,6 +1015,7 @@
opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
opt.setFrozen(false);
+ mFrozenProcesses.delete(pid);
} catch (Exception e) {
Slog.e(TAG_AM, "Unable to unfreeze " + pid + " " + app.processName
+ ". This might cause inconsistency or UI hangs.");
@@ -1019,7 +1036,7 @@
* To be called when the given app is killed.
*/
@GuardedBy({"mAm", "mProcLock"})
- void unscheduleFreezeAppLSP(ProcessRecord app) {
+ void onCleanupApplicationRecordLocked(ProcessRecord app) {
if (mUseFreezer) {
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
if (opt.isPendingFreeze()) {
@@ -1027,6 +1044,8 @@
mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
opt.setPendingFreeze(false);
}
+
+ mFrozenProcesses.delete(app.getPid());
}
}
@@ -1291,7 +1310,8 @@
}
}
- private final class FreezeHandler extends Handler {
+ private final class FreezeHandler extends Handler implements
+ ProcLocksReader.ProcLocksReaderCallback {
private FreezeHandler() {
super(mCachedAppOptimizerThread.getLooper());
}
@@ -1334,20 +1354,6 @@
opt.setPendingFreeze(false);
- try {
- // pre-check for locks to avoid unnecessary freeze/unfreeze operations
- if (mProcLocksReader.hasFileLocks(pid)) {
- if (DEBUG_FREEZER) {
- Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, not freezing");
- }
- return;
- }
- } catch (Exception e) {
- Slog.e(TAG_AM, "Not freezing. Unable to check file locks for " + name + "(" + pid
- + "): " + e);
- return;
- }
-
synchronized (mProcLock) {
pid = proc.getPid();
if (proc.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ
@@ -1401,6 +1407,7 @@
opt.setFreezeUnfreezeTime(SystemClock.uptimeMillis());
opt.setFrozen(true);
+ mFrozenProcesses.put(pid, proc);
} catch (Exception e) {
Slog.w(TAG_AM, "Unable to freeze " + pid + " " + name);
}
@@ -1448,16 +1455,13 @@
}
try {
- // post-check to prevent races
- if (mProcLocksReader.hasFileLocks(pid)) {
- if (DEBUG_FREEZER) {
- Slog.d(TAG_AM, name + " (" + pid + ") holds file locks, reverting freeze");
- }
- unfreezeAppLSP(proc);
- }
+ // post-check to prevent deadlock
+ mProcLocksReader.handleBlockingFileLocks(this);
} catch (Exception e) {
Slog.e(TAG_AM, "Unable to check file locks for " + name + "(" + pid + "): " + e);
- unfreezeAppLSP(proc);
+ synchronized (mProcLock) {
+ unfreezeAppLSP(proc);
+ }
}
}
@@ -1475,6 +1479,21 @@
frozenDuration);
}
}
+
+ @GuardedBy({"mAm"})
+ @Override
+ public void onBlockingFileLock(int pid) {
+ if (DEBUG_FREEZER) {
+ Slog.d(TAG_AM, "Process (pid=" + pid + ") holds blocking file lock");
+ }
+ synchronized (mProcLock) {
+ ProcessRecord app = mFrozenProcesses.get(pid);
+ if (app != null) {
+ Slog.i(TAG_AM, app.processName + " (" + pid + ") holds blocking file lock");
+ unfreezeAppLSP(app);
+ }
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/CriticalEventLog.java b/services/core/java/com/android/server/am/CriticalEventLog.java
new file mode 100644
index 0000000..6b69559
--- /dev/null
+++ b/services/core/java/com/android/server/am/CriticalEventLog.java
@@ -0,0 +1,328 @@
+/*
+ * 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.am;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Slog;
+
+import com.android.framework.protobuf.nano.MessageNanoPrinter;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.RingBuffer;
+import com.android.server.am.nano.CriticalEventLogProto;
+import com.android.server.am.nano.CriticalEventLogStorageProto;
+import com.android.server.am.nano.CriticalEventProto;
+import com.android.server.am.nano.CriticalEventProto.HalfWatchdog;
+import com.android.server.am.nano.CriticalEventProto.Watchdog;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.UUID;
+
+/**
+ * Log of recent critical events such as Watchdogs.
+ *
+ * For use in ANR reports to show recent events that may help to debug the ANR. In particular,
+ * the presence of recent critical events signal that the device was already in a had state.
+ *
+ * This class needs to be thread safe since it's used as a singleton.
+ */
+public class CriticalEventLog {
+ private static final String TAG = CriticalEventLog.class.getSimpleName();
+
+ private static CriticalEventLog sInstance;
+
+ /** Name of the file the log is saved to. */
+ @VisibleForTesting
+ static final String FILENAME = "critical_event_log.pb";
+
+ /** Timestamp when the log was last saved (or attempted to be saved) to disk. */
+ private long mLastSaveAttemptMs = 0;
+
+ /** File the log is saved to. */
+ private final File mLogFile;
+
+ /** Ring buffer containing the log events. */
+ private final ThreadSafeRingBuffer<CriticalEventProto> mEvents;
+
+ /** Max age of events to include in the output log proto. */
+ private final int mWindowMs;
+
+ /** Minimum time between consecutive saves of the log to disk. */
+ private final long mMinTimeBetweenSavesMs;
+
+ /** Whether to load and save the log synchronously with no delay. Only set to true in tests. */
+ private final boolean mLoadAndSaveImmediately;
+
+ private final Handler mHandler;
+
+ private final Runnable mSaveRunnable = this::saveLogToFileNow;
+
+ @VisibleForTesting
+ CriticalEventLog(String logDir, int capacity, int windowMs, long minTimeBetweenSavesMs,
+ boolean loadAndSaveImmediately, ILogLoader logLoader) {
+ mLogFile = Paths.get(logDir, FILENAME).toFile();
+ mWindowMs = windowMs;
+ mMinTimeBetweenSavesMs = minTimeBetweenSavesMs;
+ mLoadAndSaveImmediately = loadAndSaveImmediately;
+
+ mEvents = new ThreadSafeRingBuffer<>(CriticalEventProto.class, capacity);
+
+ HandlerThread thread = new HandlerThread("CriticalEventLogIO");
+ thread.start();
+ mHandler = new Handler(thread.getLooper());
+
+ final Runnable loadEvents = () -> logLoader.load(mLogFile, mEvents);
+ if (!mLoadAndSaveImmediately) {
+ mHandler.post(loadEvents);
+ } else {
+ loadEvents.run();
+ }
+ }
+
+ /** Returns a new instance with production settings. */
+ private CriticalEventLog() {
+ this(
+ /* logDir= */"/data/misc/critical-events",
+ /* capacity= */ 20,
+ /* windowMs= */ (int) Duration.ofMinutes(5).toMillis(),
+ /* minTimeBetweenSavesMs= */ Duration.ofSeconds(2).toMillis(),
+ /* loadAndSaveImmediately= */ false,
+ new LogLoader());
+ }
+
+ /** Returns the singleton instance. */
+ public static CriticalEventLog getInstance() {
+ if (sInstance == null) {
+ sInstance = new CriticalEventLog();
+ }
+ return sInstance;
+ }
+
+ /**
+ * Ensures the singleton instance has been instantiated.
+ *
+ * Use this to eagerly instantiate the log (which loads the previous events from disk).
+ * Otherwise this will occur lazily when the first event is logged, by which time the device may
+ * be under load.
+ */
+ public static void init() {
+ getInstance();
+ }
+
+ @VisibleForTesting
+ protected long getWallTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ /** Logs a watchdog. */
+ public void logWatchdog(String subject, UUID uuid) {
+ Watchdog watchdog = new Watchdog();
+ watchdog.subject = subject;
+ watchdog.uuid = uuid.toString();
+ CriticalEventProto event = new CriticalEventProto();
+ event.setWatchdog(watchdog);
+ log(event);
+ }
+
+ /** Logs a half-watchdog. */
+ public void logHalfWatchdog(String subject) {
+ HalfWatchdog halfWatchdog = new HalfWatchdog();
+ halfWatchdog.subject = subject;
+ CriticalEventProto event = new CriticalEventProto();
+ event.setHalfWatchdog(halfWatchdog);
+ log(event);
+ }
+
+ private void log(CriticalEventProto event) {
+ event.timestampMs = getWallTimeMillis();
+ mEvents.append(event);
+ saveLogToFile();
+ }
+
+ /**
+ * Returns recent critical events in text format to include in logs such as ANR files.
+ *
+ * Includes all events in the ring buffer with age less than or equal to {@code mWindowMs}.
+ */
+ public String logLinesForAnrFile() {
+ return new StringBuilder()
+ .append("--- CriticalEventLog ---\n")
+ .append(MessageNanoPrinter.print(getRecentEvents()))
+ .append('\n').toString();
+ }
+
+ /**
+ * Returns a proto containing recent critical events.
+ *
+ * Includes all events in the ring buffer with age less than or equal to {@code mWindowMs}.
+ */
+ @VisibleForTesting
+ protected CriticalEventLogProto getRecentEvents() {
+ CriticalEventLogProto log = new CriticalEventLogProto();
+ log.timestampMs = getWallTimeMillis();
+ log.windowMs = mWindowMs;
+ log.capacity = mEvents.capacity();
+ log.events = recentEventsWithMinTimestamp(log.timestampMs - mWindowMs);
+
+ return log;
+ }
+
+ /**
+ * Returns the most recent logged events, starting with the first event that has a timestamp
+ * greater than or equal to {@code minTimestampMs}.
+ *
+ * If no events have a timestamp greater than or equal to {@code minTimestampMs}, returns an
+ * empty array.
+ */
+ private CriticalEventProto[] recentEventsWithMinTimestamp(long minTimestampMs) {
+ // allEvents are in insertion order, i.e. in order of when the relevant log___() function
+ // was called.
+ // This means that if the system clock changed (e.g. a NITZ update) allEvents may not be
+ // strictly ordered by timestamp. In this case we are permissive and start with the
+ // first event that has a timestamp in the desired range.
+ CriticalEventProto[] allEvents = mEvents.toArray();
+ for (int i = 0; i < allEvents.length; i++) {
+ if (allEvents[i].timestampMs >= minTimestampMs) {
+ return Arrays.copyOfRange(allEvents, i, allEvents.length);
+ }
+ }
+ return new CriticalEventProto[]{};
+ }
+
+ private void saveLogToFile() {
+ if (mLoadAndSaveImmediately) {
+ saveLogToFileNow();
+ return;
+ }
+ if (mHandler.hasCallbacks(mSaveRunnable)) {
+ // An earlier save is already scheduled so don't need to schedule an additional one.
+ return;
+ }
+
+ if (!mHandler.postDelayed(mSaveRunnable, saveDelayMs())) {
+ Slog.w(TAG, "Error scheduling save");
+ }
+ }
+
+ /**
+ * Returns the delay in milliseconds when scheduling a save on the handler thread.
+ *
+ * Returns a value in the range [0, {@code minTimeBetweenSavesMs}] such that the time between
+ * consecutive saves does not exceed {@code minTimeBetweenSavesMs}.
+ *
+ * This means that if the last save occurred a long time ago, or if no previous saves
+ * have occurred then the returned delay will be zero.
+ */
+ @VisibleForTesting
+ protected long saveDelayMs() {
+ final long nowMs = getWallTimeMillis();
+ return Math.max(0,
+ mLastSaveAttemptMs + mMinTimeBetweenSavesMs - nowMs);
+ }
+
+ @VisibleForTesting
+ protected void saveLogToFileNow() {
+ mLastSaveAttemptMs = getWallTimeMillis();
+
+ File logDir = mLogFile.getParentFile();
+ if (!logDir.exists()) {
+ if (!logDir.mkdir()) {
+ Slog.e(TAG, "Error creating log directory: " + logDir.getPath());
+ return;
+ }
+ }
+
+ if (!mLogFile.exists()) {
+ try {
+ mLogFile.createNewFile();
+ } catch (IOException e) {
+ Slog.e(TAG, "Error creating log file", e);
+ return;
+ }
+ }
+
+ CriticalEventLogStorageProto logProto = new CriticalEventLogStorageProto();
+ logProto.events = mEvents.toArray();
+
+ final byte[] bytes = CriticalEventLogStorageProto.toByteArray(logProto);
+ try (FileOutputStream stream = new FileOutputStream(mLogFile, false)) {
+ stream.write(bytes);
+ } catch (IOException e) {
+ Slog.e(TAG, "Error saving log to disk.", e);
+ }
+ }
+
+ @VisibleForTesting
+ protected static class ThreadSafeRingBuffer<T> {
+ private final int mCapacity;
+ private final RingBuffer<T> mBuffer;
+
+ ThreadSafeRingBuffer(Class<T> clazz, int capacity) {
+ this.mCapacity = capacity;
+ this.mBuffer = new RingBuffer<>(clazz, capacity);
+ }
+
+ synchronized void append(T t) {
+ mBuffer.append(t);
+ }
+
+ synchronized T[] toArray() {
+ return mBuffer.toArray();
+ }
+
+ int capacity() {
+ return mCapacity;
+ }
+ }
+
+ /** Loads log events from disk into a ring buffer. */
+ protected interface ILogLoader {
+ void load(File logFile, ThreadSafeRingBuffer<CriticalEventProto> buffer);
+ }
+
+ /** Loads log events from disk into a ring buffer. */
+ static class LogLoader implements ILogLoader {
+ @Override
+ public void load(File logFile,
+ ThreadSafeRingBuffer<CriticalEventProto> buffer) {
+ for (CriticalEventProto event : loadLogFromFile(logFile).events) {
+ buffer.append(event);
+ }
+ }
+
+ private static CriticalEventLogStorageProto loadLogFromFile(File logFile) {
+ if (!logFile.exists()) {
+ Slog.i(TAG, "No log found, returning empty log proto.");
+ return new CriticalEventLogStorageProto();
+ }
+
+ try {
+ return CriticalEventLogStorageProto.parseFrom(
+ Files.readAllBytes(logFile.toPath()));
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading log from disk.", e);
+ return new CriticalEventLogStorageProto();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 81a8680..b1c91ba 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -368,7 +368,8 @@
// Apply any launch flags from the ActivityOptions. This is to ensure that the caller
// can specify a consistent launch mode even if the PendingIntent is immutable
- final ActivityOptions opts = ActivityOptions.fromBundle(options);
+ final ActivityOptions opts = options != null ? ActivityOptions.fromBundle(options)
+ : null;
if (opts != null) {
finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index a32fe02..5fac879 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -400,9 +400,10 @@
StringWriter tracesFileException = new StringWriter();
// To hold the start and end offset to the ANR trace file respectively.
final long[] offsets = new long[2];
+ final String criticalEventLog = CriticalEventLog.getInstance().logLinesForAnrFile();
File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
- nativePids, tracesFileException, offsets, annotation);
+ nativePids, tracesFileException, offsets, annotation, criticalEventLog);
if (isMonitorCpuUsage()) {
mService.updateCpuStatsNow();
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 2bf1ccd..fb74170 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -81,8 +81,11 @@
private final OverrideValidatorImpl mOverrideValidator;
private final AndroidBuildClassifier mAndroidBuildClassifier;
private Context mContext;
- @GuardedBy("mOverridesFile")
+ private final Object mOverridesFileLock = new Object();
+ @GuardedBy("mOverridesFileLock")
private File mOverridesFile;
+ @GuardedBy("mOverridesFileLock")
+ private File mBackupOverridesFile;
@VisibleForTesting
CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
@@ -550,8 +553,15 @@
loadOverrides(staticOverridesFile);
- mOverridesFile = dynamicOverridesFile;
- loadOverrides(dynamicOverridesFile);
+ synchronized (mOverridesFileLock) {
+ mOverridesFile = dynamicOverridesFile;
+ mBackupOverridesFile = makeBackupFile(dynamicOverridesFile);
+ if (mBackupOverridesFile.exists()) {
+ mOverridesFile.delete();
+ mBackupOverridesFile.renameTo(mOverridesFile);
+ }
+ loadOverrides(mOverridesFile);
+ }
if (staticOverridesFile.exists()) {
// Only save overrides if there is a static overrides file.
@@ -559,6 +569,10 @@
}
}
+ private File makeBackupFile(File overridesFile) {
+ return new File(overridesFile.getPath() + ".bak");
+ }
+
private void loadOverrides(File overridesFile) {
if (!overridesFile.exists()) {
// Overrides file doesn't exist.
@@ -591,10 +605,11 @@
* Persist compat framework overrides to /data/misc/appcompat/compat_framework_overrides.xml
*/
void saveOverrides() {
- if (mOverridesFile == null) {
- return;
- }
- synchronized (mOverridesFile) {
+ synchronized (mOverridesFileLock) {
+ if (mOverridesFile == null || mBackupOverridesFile == null) {
+ return;
+ }
+
Overrides overrides = new Overrides();
List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides();
for (CompatChange c : mChanges.values()) {
@@ -603,6 +618,20 @@
changeOverridesList.add(changeOverrides);
}
}
+
+ // Rename the file to the backup.
+ if (mOverridesFile.exists()) {
+ if (mBackupOverridesFile.exists()) {
+ mOverridesFile.delete();
+ } else {
+ if (!mOverridesFile.renameTo(mBackupOverridesFile)) {
+ Slog.e(TAG, "Couldn't rename file " + mOverridesFile
+ + " to " + mBackupOverridesFile);
+ return;
+ }
+ }
+ }
+
// Create the file if it doesn't already exist
try {
mOverridesFile.createNewFile();
@@ -616,6 +645,9 @@
} catch (IOException e) {
Slog.e(TAG, e.toString());
}
+
+ // Remove the backup if the write succeeds.
+ mBackupOverridesFile.delete();
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 87c27c9..045ee8a 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -826,7 +826,7 @@
Settings.Global.USER_PREFERRED_RESOLUTION_HEIGHT, -1);
final int width = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, -1);
- Display.Mode mode = new Display.Mode(height, width, refreshRate);
+ Display.Mode mode = new Display.Mode(width, height, refreshRate);
mUserPreferredMode = isResolutionAndRefreshRateValid(mode) ? mode : null;
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index fce3fd53..d6c28e2 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -54,6 +54,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayInfo;
@@ -77,7 +78,6 @@
import java.util.Locale;
import java.util.Objects;
-
/**
* The DisplayModeDirector is responsible for determining what modes are allowed to be automatically
* picked by the system based on system-wide and display-specific configuration.
@@ -92,6 +92,8 @@
private static final int MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED = 4;
private static final int MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED = 5;
private static final int MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED = 6;
+ private static final int MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED = 7;
+ private static final int MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED = 8;
// Special ID used to indicate that given vote is to be applied globally, rather than to a
// specific display.
@@ -159,9 +161,10 @@
}
};
mSensorObserver = new SensorObserver(context, ballotBox, injector);
- mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler());
mSkinThermalStatusObserver = new SkinThermalStatusObserver(injector, ballotBox);
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
+ mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler(),
+ mDeviceConfigDisplaySettings);
mDeviceConfig = injector.getDeviceConfig();
mAlwaysRespectAppRequest = false;
}
@@ -722,6 +725,11 @@
}
@VisibleForTesting
+ HbmObserver getHbmObserver() {
+ return mHbmObserver;
+ }
+
+ @VisibleForTesting
DesiredDisplayModeSpecs getDesiredDisplayModeSpecsWithInjectedFpsSettings(
float minRefreshRate, float peakRefreshRate, float defaultRefreshRate) {
synchronized (mLock) {
@@ -790,6 +798,19 @@
(DesiredDisplayModeSpecsListener) msg.obj;
desiredDisplayModeSpecsListener.onDesiredDisplayModeSpecsChanged();
break;
+
+ case MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED: {
+ int refreshRateInHbmSunlight = msg.arg1;
+ mHbmObserver.onDeviceConfigRefreshRateInHbmSunlightChanged(
+ refreshRateInHbmSunlight);
+ break;
+ }
+
+ case MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED: {
+ int refreshRateInHbmHdr = msg.arg1;
+ mHbmObserver.onDeviceConfigRefreshRateInHbmHdrChanged(refreshRateInHbmHdr);
+ break;
+ }
}
}
}
@@ -916,16 +937,19 @@
// result is a range.
public static final int PRIORITY_FLICKER_REFRESH_RATE = 1;
+ // High-brightness-mode may need a specific range of refresh-rates to function properly.
+ public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 2;
+
// SETTING_MIN_REFRESH_RATE is used to propose a lower bound of display refresh rate.
// It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY]
- public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 2;
+ public static final int PRIORITY_USER_SETTING_MIN_REFRESH_RATE = 3;
// APP_REQUEST_REFRESH_RATE_RANGE is used to for internal apps to limit the refresh
// rate in certain cases, mostly to preserve power.
// @see android.view.WindowManager.LayoutParams#preferredMinRefreshRate
// @see android.view.WindowManager.LayoutParams#preferredMaxRefreshRate
// It votes to [preferredMinRefreshRate, preferredMaxRefreshRate].
- public static final int PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE = 3;
+ public static final int PRIORITY_APP_REQUEST_REFRESH_RATE_RANGE = 4;
// We split the app request into different priorities in case we can satisfy one desire
// without the other.
@@ -940,27 +964,24 @@
// The preferred refresh rate is set on the main surface of the app outside of
// DisplayModeDirector.
// @see com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded
- public static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 4;
- public static final int PRIORITY_APP_REQUEST_SIZE = 5;
+ public static final int PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE = 5;
+ public static final int PRIORITY_APP_REQUEST_SIZE = 6;
// SETTING_PEAK_REFRESH_RATE has a high priority and will restrict the bounds of the rest
// of low priority voters. It votes [0, max(PEAK, MIN)]
- public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 6;
+ public static final int PRIORITY_USER_SETTING_PEAK_REFRESH_RATE = 7;
// LOW_POWER_MODE force display to [0, 60HZ] if Settings.Global.LOW_POWER_MODE is on.
- public static final int PRIORITY_LOW_POWER_MODE = 7;
+ public static final int PRIORITY_LOW_POWER_MODE = 8;
// PRIORITY_FLICKER_REFRESH_RATE_SWITCH votes for disabling refresh rate switching. If the
// higher priority voters' result is a range, it will fix the rate to a single choice.
// It's used to avoid refresh rate switches in certain conditions which may result in the
// user seeing the display flickering when the switches occur.
- public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8;
+ public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 9;
// Force display to [0, 60HZ] if skin temperature is at or above CRITICAL.
- public static final int PRIORITY_SKIN_TEMPERATURE = 9;
-
- // High-brightness-mode may need a specific range of refresh-rates to function properly.
- public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 10;
+ public static final int PRIORITY_SKIN_TEMPERATURE = 10;
// The proximity sensor needs the refresh rate to be locked in order to function, so this is
// set to a high priority.
@@ -2258,33 +2279,78 @@
* HBM that are associated with that display. Restrictions are retrieved from
* DisplayManagerInternal but originate in the display-device-config file.
*/
- private static class HbmObserver implements DisplayManager.DisplayListener {
+ public static class HbmObserver implements DisplayManager.DisplayListener {
private final BallotBox mBallotBox;
private final Handler mHandler;
- private final SparseBooleanArray mHbmEnabled = new SparseBooleanArray();
+ private final SparseIntArray mHbmMode = new SparseIntArray();
private final Injector mInjector;
+ private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
+ private int mRefreshRateInHbmSunlight;
+ private int mRefreshRateInHbmHdr;
private DisplayManagerInternal mDisplayManagerInternal;
- HbmObserver(Injector injector, BallotBox ballotBox, Handler handler) {
+ HbmObserver(Injector injector, BallotBox ballotBox, Handler handler,
+ DeviceConfigDisplaySettings displaySettings) {
mInjector = injector;
mBallotBox = ballotBox;
mHandler = handler;
+ mDeviceConfigDisplaySettings = displaySettings;
}
public void observe() {
+ mRefreshRateInHbmSunlight = mDeviceConfigDisplaySettings.getRefreshRateInHbmSunlight();
+ mRefreshRateInHbmHdr = mDeviceConfigDisplaySettings.getRefreshRateInHbmHdr();
+
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mInjector.registerDisplayListener(this, mHandler,
DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED);
}
+ /**
+ * @return the refresh to lock to when the device is in high brightness mode for Sunlight.
+ */
+ @VisibleForTesting
+ int getRefreshRateInHbmSunlight() {
+ return mRefreshRateInHbmSunlight;
+ }
+
+ /**
+ * @return the refresh to lock to when the device is in high brightness mode for HDR.
+ */
+ @VisibleForTesting
+ int getRefreshRateInHbmHdr() {
+ return mRefreshRateInHbmHdr;
+ }
+
+ /**
+ * Recalculates the HBM vote when the device config has been changed.
+ */
+ public void onDeviceConfigRefreshRateInHbmSunlightChanged(int refreshRate) {
+ if (refreshRate != mRefreshRateInHbmSunlight) {
+ mRefreshRateInHbmSunlight = refreshRate;
+ onDeviceConfigRefreshRateInHbmChanged();
+ }
+ }
+
+ /**
+ * Recalculates the HBM vote when the device config has been changed.
+ */
+ public void onDeviceConfigRefreshRateInHbmHdrChanged(int refreshRate) {
+ if (refreshRate != mRefreshRateInHbmHdr) {
+ mRefreshRateInHbmHdr = refreshRate;
+ onDeviceConfigRefreshRateInHbmChanged();
+ }
+ }
+
@Override
public void onDisplayAdded(int displayId) {}
@Override
public void onDisplayRemoved(int displayId) {
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, null);
+ mHbmMode.delete(displayId);
}
@Override
@@ -2294,31 +2360,56 @@
// Display no longer there. Assume we'll get an onDisplayRemoved very soon.
return;
}
- final boolean isHbmEnabled =
- info.highBrightnessMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
- if (isHbmEnabled == mHbmEnabled.get(displayId)) {
+ final int hbmMode = info.highBrightnessMode;
+ if (hbmMode == mHbmMode.get(displayId)) {
// no change, ignore.
return;
}
+ mHbmMode.put(displayId, hbmMode);
+ recalculateVotesForDisplay(displayId);
+ }
+
+ private void onDeviceConfigRefreshRateInHbmChanged() {
+ final int[] displayIds = mHbmMode.copyKeys();
+ if (displayIds != null) {
+ for (int id : displayIds) {
+ recalculateVotesForDisplay(id);
+ }
+ }
+ }
+
+ private void recalculateVotesForDisplay(int displayId) {
+ final int hbmMode = mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
Vote vote = null;
- mHbmEnabled.put(displayId, isHbmEnabled);
- if (isHbmEnabled) {
- final List<RefreshRateLimitation> limits =
+ if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) {
+ // Device resource properties take priority over DisplayDeviceConfig
+ if (mRefreshRateInHbmSunlight > 0) {
+ vote = Vote.forRefreshRates(mRefreshRateInHbmSunlight,
+ mRefreshRateInHbmSunlight);
+ } else {
+ final List<RefreshRateLimitation> limits =
mDisplayManagerInternal.getRefreshRateLimitations(displayId);
- for (int i = 0; limits != null && i < limits.size(); i++) {
- final RefreshRateLimitation limitation = limits.get(i);
- if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
- vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max);
- break;
+ for (int i = 0; limits != null && i < limits.size(); i++) {
+ final RefreshRateLimitation limitation = limits.get(i);
+ if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) {
+ vote = Vote.forRefreshRates(limitation.range.min, limitation.range.max);
+ break;
+ }
}
}
}
+ if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
+ && mRefreshRateInHbmHdr > 0) {
+ vote = Vote.forRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr);
+ }
mBallotBox.vote(displayId, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE, vote);
}
void dumpLocked(PrintWriter pw) {
pw.println(" HbmObserver");
- pw.println(" mHbmEnabled: " + mHbmEnabled);
+ pw.println(" mHbmMode: " + mHbmMode);
+ pw.println(" mRefreshRateInHbmSunlight: " + mRefreshRateInHbmSunlight);
+ pw.println(" mRefreshRateInHbmHdr: " + mRefreshRateInHbmHdr);
}
}
@@ -2437,6 +2528,29 @@
return refreshRate;
}
+ public int getRefreshRateInHbmSunlight() {
+ final int defaultRefreshRateInHbmSunlight =
+ mContext.getResources().getInteger(
+ R.integer.config_defaultRefreshRateInHbmSunlight);
+
+ final int refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT,
+ defaultRefreshRateInHbmSunlight);
+
+ return refreshRate;
+ }
+
+ public int getRefreshRateInHbmHdr() {
+ final int defaultRefreshRateInHbmHdr =
+ mContext.getResources().getInteger(R.integer.config_defaultRefreshRateInHbmHdr);
+
+ final int refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR,
+ defaultRefreshRateInHbmHdr);
+
+ return refreshRate;
+ }
+
/*
* Return null if no such property
*/
@@ -2476,6 +2590,15 @@
.sendToTarget();
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HIGH_ZONE_CHANGED, refreshRateInHighZone, 0)
.sendToTarget();
+
+ final int refreshRateInHbmSunlight = getRefreshRateInHbmSunlight();
+ mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_SUNLIGHT_CHANGED,
+ refreshRateInHbmSunlight, 0)
+ .sendToTarget();
+
+ final int refreshRateInHbmHdr = getRefreshRateInHbmHdr();
+ mHandler.obtainMessage(MSG_REFRESH_RATE_IN_HBM_HDR_CHANGED, refreshRateInHbmHdr, 0)
+ .sendToTarget();
}
private int[] getIntArrayProperty(String prop) {
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectActionFromPlayback.java b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromPlayback.java
index b2338e6..1bacca6 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectActionFromPlayback.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromPlayback.java
@@ -102,7 +102,7 @@
mIsCec20 = isCec20;
}
- private int getTargetAddress() {
+ int getTargetAddress() {
return mTarget.getLogicalAddress();
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 6736d2a..cc86430 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -324,7 +324,8 @@
/**
* Return the physical address of the device.
*
- * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+ * <p>Declared as package-private. accessed by {@link HdmiControlService} and
+ * {@link HdmiCecNetwork} only.
*
* @return CEC physical address of the device. The range of success address
* is between 0x0000 and 0xFFFF. If failed it returns -1
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index f94d220..ab8217a 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -274,6 +274,13 @@
return false;
}
+ // Clear all device info.
+ @ServiceThreadOnly
+ void clearDeviceInfoList() {
+ assertRunOnServiceThread();
+ mService.getHdmiCecNetwork().clearDeviceList();
+ }
+
@ServiceThreadOnly
@Constants.HandleMessageResult
protected final int onMessage(HdmiCecMessage message) {
@@ -787,10 +794,10 @@
byte[] params = message.getParams();
return message.getOpcode() == Constants.MESSAGE_USER_CONTROL_PRESSED
&& (params[0] == HdmiCecKeycode.CEC_KEYCODE_VOLUME_DOWN
- || params[0] == HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP
- || params[0] == HdmiCecKeycode.CEC_KEYCODE_MUTE
- || params[0] == HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION
- || params[0] == HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION);
+ || params[0] == HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP
+ || params[0] == HdmiCecKeycode.CEC_KEYCODE_MUTE
+ || params[0] == HdmiCecKeycode.CEC_KEYCODE_MUTE_FUNCTION
+ || params[0] == HdmiCecKeycode.CEC_KEYCODE_RESTORE_VOLUME_FUNCTION);
}
@Constants.HandleMessageResult
@@ -1243,13 +1250,13 @@
|| logicalAddress == mDeviceInfo.getLogicalAddress()) {
// Don't send key event to invalid device or itself.
Slog.w(
- TAG,
- "Discard volume key event: "
- + keyCode
- + ", pressed:"
- + isPressed
- + ", receiverAddr="
- + logicalAddress);
+ TAG,
+ "Discard volume key event: "
+ + keyCode
+ + ", pressed:"
+ + isPressed
+ + ", receiverAddr="
+ + logicalAddress);
} else if (!action.isEmpty()) {
action.get(0).processKeyEvent(keyCode, isPressed);
} else if (isPressed) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 744436d..d6ac25a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -105,9 +105,42 @@
}
});
}
+ launchDeviceDiscovery();
startQueuedActions();
}
+ @ServiceThreadOnly
+ private void launchDeviceDiscovery() {
+ assertRunOnServiceThread();
+ clearDeviceInfoList();
+ DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
+ new DeviceDiscoveryAction.DeviceDiscoveryCallback() {
+ @Override
+ public void onDeviceDiscoveryDone(List<HdmiDeviceInfo> deviceInfos) {
+ for (HdmiDeviceInfo info : deviceInfos) {
+ mService.getHdmiCecNetwork().addCecDevice(info);
+ }
+
+ // Since we removed all devices when it starts and device discovery action
+ // does not poll local devices, we should put device info of local device
+ // manually here.
+ for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
+ synchronized (device.mLock) {
+ mService.getHdmiCecNetwork().addCecDevice(device.getDeviceInfo());
+ }
+ }
+
+ List<HotplugDetectionAction> hotplugActions =
+ getActions(HotplugDetectionAction.class);
+ if (hotplugActions.isEmpty()) {
+ addAndStartAction(
+ new HotplugDetectionAction(HdmiCecLocalDevicePlayback.this));
+ }
+ }
+ });
+ addAndStartAction(action);
+ }
+
@Override
@ServiceThreadOnly
protected int getPreferredAddress() {
@@ -450,9 +483,12 @@
@Override
@ServiceThreadOnly
protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) {
- super.disableDevice(initiatedByCec, callback);
-
assertRunOnServiceThread();
+ removeAction(DeviceDiscoveryAction.class);
+ removeAction(HotplugDetectionAction.class);
+ removeAction(NewDeviceAction.class);
+ super.disableDevice(initiatedByCec, callback);
+ clearDeviceInfoList();
checkIfPendingActionsCleared();
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 8d0a7bd..aba7d39 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -685,7 +685,7 @@
mService.getHdmiCecNetwork().addCecDevice(info);
}
- // Since we removed all devices when it's start and
+ // Since we removed all devices when it starts and
// device discovery action does not poll local devices,
// we should put device info of local device manually here
for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
@@ -734,13 +734,6 @@
}
}
- // Clear all device info.
- @ServiceThreadOnly
- private void clearDeviceInfoList() {
- assertRunOnServiceThread();
- mService.getHdmiCecNetwork().clearDeviceList();
- }
-
@ServiceThreadOnly
// Seq #32
void changeSystemAudioMode(boolean enabled, IHdmiControlCallback callback) {
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index 6fd7a72..da40ce59 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -28,16 +28,19 @@
* Feature action that handles hot-plug detection mechanism.
* Hot-plug event is initiated by timer after device discovery action.
*
- * <p>Check all devices every 15 secs except for system audio.
+ * <p>TV checks all devices every 15 secs except for system audio.
* If system audio is on, check hot-plug for audio system every 5 secs.
* For other devices, keep 15 secs period.
+ *
+ * <p>Playback devices check all devices every 1 minute.
*/
// Seq #3
final class HotplugDetectionAction extends HdmiCecFeatureAction {
private static final String TAG = "HotPlugDetectionAction";
- private static final int POLLING_INTERVAL_MS = 5000;
- private static final int TIMEOUT_COUNT = 3;
+ public static final int POLLING_INTERVAL_MS_FOR_TV = 5000;
+ public static final int POLLING_INTERVAL_MS_FOR_PLAYBACK = 60000;
+ public static final int TIMEOUT_COUNT = 3;
private static final int AVR_COUNT_MAX = 3;
// State in which waits for next polling
@@ -55,6 +58,8 @@
// is detected {@code AVR_COUNT_MAX} times in a row.
private int mAvrStatusCount = 0;
+ private final boolean mIsTvDevice = localDevice().mService.isTvDevice();
+
/**
* Constructor
*
@@ -64,16 +69,21 @@
super(source);
}
+ private int getPollingInterval() {
+ return mIsTvDevice ? POLLING_INTERVAL_MS_FOR_TV : POLLING_INTERVAL_MS_FOR_PLAYBACK;
+ }
+
@Override
boolean start() {
- Slog.v(TAG, "Hot-plug dection started.");
+ Slog.v(TAG, "Hot-plug detection started.");
mState = STATE_WAIT_FOR_NEXT_POLLING;
mTimeoutCount = 0;
// Start timer without polling.
- // The first check for all devices will be initiated 15 seconds later.
- addTimer(mState, POLLING_INTERVAL_MS);
+ // The first check for all devices will be initiated 15 seconds later for TV panels and 60
+ // seconds later for playback devices.
+ addTimer(mState, getPollingInterval());
return true;
}
@@ -90,13 +100,24 @@
}
if (mState == STATE_WAIT_FOR_NEXT_POLLING) {
- mTimeoutCount = (mTimeoutCount + 1) % TIMEOUT_COUNT;
- pollDevices();
+ if (mIsTvDevice) {
+ mTimeoutCount = (mTimeoutCount + 1) % TIMEOUT_COUNT;
+ if (mTimeoutCount == 0) {
+ pollAllDevices();
+ } else if (tv().isSystemAudioActivated()) {
+ pollAudioSystem();
+ }
+ addTimer(mState, POLLING_INTERVAL_MS_FOR_TV);
+ return;
+ }
+ pollAllDevices();
+ addTimer(mState, POLLING_INTERVAL_MS_FOR_PLAYBACK);
}
}
/**
- * Start device polling immediately.
+ * Start device polling immediately. This method is called only by
+ * {@link HdmiCecLocalDeviceTv#onHotplug}.
*/
void pollAllDevicesNow() {
// Clear existing timer to avoid overlapped execution
@@ -106,21 +127,7 @@
mState = STATE_WAIT_FOR_NEXT_POLLING;
pollAllDevices();
- addTimer(mState, POLLING_INTERVAL_MS);
- }
-
- // This method is called every 5 seconds.
- private void pollDevices() {
- // All device check called every 15 seconds.
- if (mTimeoutCount == 0) {
- pollAllDevices();
- } else {
- if (tv().isSystemAudioActivated()) {
- pollAudioSystem();
- }
- }
-
- addTimer(mState, POLLING_INTERVAL_MS);
+ addTimer(mState, getPollingInterval());
}
private void pollAllDevices() {
@@ -148,15 +155,16 @@
}
private void checkHotplug(List<Integer> ackedAddress, boolean audioOnly) {
- BitSet currentInfos = infoListToBitSet(
- localDevice().mService.getHdmiCecNetwork().getDeviceInfoList(false), audioOnly);
+ List<HdmiDeviceInfo> deviceInfoList =
+ localDevice().mService.getHdmiCecNetwork().getDeviceInfoList(false);
+ BitSet currentInfos = infoListToBitSet(deviceInfoList, audioOnly, false);
BitSet polledResult = addressListToBitSet(ackedAddress);
// At first, check removed devices.
BitSet removed = complement(currentInfos, polledResult);
int index = -1;
while ((index = removed.nextSetBit(index + 1)) != -1) {
- if (index == Constants.ADDR_AUDIO_SYSTEM) {
+ if (mIsTvDevice && index == Constants.ADDR_AUDIO_SYSTEM) {
HdmiDeviceInfo avr = tv().getAvrDeviceInfo();
if (avr != null && tv().isConnected(avr.getPortId())) {
++mAvrStatusCount;
@@ -176,7 +184,8 @@
}
// Next, check added devices.
- BitSet added = complement(polledResult, currentInfos);
+ BitSet currentInfosWithPhysicalAddress = infoListToBitSet(deviceInfoList, audioOnly, true);
+ BitSet added = complement(polledResult, currentInfosWithPhysicalAddress);
index = -1;
while ((index = added.nextSetBit(index + 1)) != -1) {
Slog.v(TAG, "Add device by hot-plug detection:" + index);
@@ -184,14 +193,15 @@
}
}
- private static BitSet infoListToBitSet(List<HdmiDeviceInfo> infoList, boolean audioOnly) {
+ private static BitSet infoListToBitSet(
+ List<HdmiDeviceInfo> infoList, boolean audioOnly, boolean requirePhysicalAddress) {
BitSet set = new BitSet(NUM_OF_ADDRESS);
for (HdmiDeviceInfo info : infoList) {
- if (audioOnly) {
- if (info.getDeviceType() == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM) {
- set.set(info.getLogicalAddress());
- }
- } else {
+ boolean audioOnlyConditionMet = !audioOnly
+ || (info.getDeviceType() == HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ boolean requirePhysicalAddressConditionMet = !requirePhysicalAddress
+ || (info.getPhysicalAddress() != HdmiDeviceInfo.PATH_INVALID);
+ if (audioOnlyConditionMet && requirePhysicalAddressConditionMet) {
set.set(info.getLogicalAddress());
}
}
@@ -221,11 +231,12 @@
}
private void removeDevice(int removedAddress) {
- mayChangeRoutingPath(removedAddress);
+ if (mIsTvDevice) {
+ mayChangeRoutingPath(removedAddress);
+ mayCancelOneTouchRecord(removedAddress);
+ mayDisableSystemAudioAndARC(removedAddress);
+ }
mayCancelDeviceSelect(removedAddress);
- mayCancelOneTouchRecord(removedAddress);
- mayDisableSystemAudioAndARC(removedAddress);
-
localDevice().mService.getHdmiCecNetwork().removeCecDevice(localDevice(), removedAddress);
}
@@ -237,15 +248,19 @@
}
private void mayCancelDeviceSelect(int address) {
- List<DeviceSelectActionFromTv> actions = getActions(DeviceSelectActionFromTv.class);
- if (actions.isEmpty()) {
- return;
+ List<DeviceSelectActionFromTv> actionsFromTv = getActions(DeviceSelectActionFromTv.class);
+ for (DeviceSelectActionFromTv action : actionsFromTv) {
+ if (action.getTargetAddress() == address) {
+ removeAction(DeviceSelectActionFromTv.class);
+ }
}
- // Should have only one Device Select Action
- DeviceSelectActionFromTv action = actions.get(0);
- if (action.getTargetAddress() == address) {
- removeAction(DeviceSelectActionFromTv.class);
+ List<DeviceSelectActionFromPlayback> actionsFromPlayback = getActions(
+ DeviceSelectActionFromPlayback.class);
+ for (DeviceSelectActionFromPlayback action : actionsFromPlayback) {
+ if (action.getTargetAddress() == address) {
+ removeAction(DeviceSelectActionFromTv.class);
+ }
}
}
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
index 66fc0d9..acf705a 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorAction.java
@@ -41,7 +41,7 @@
private static final int INVALID_POWER_STATUS = POWER_STATUS_UNKNOWN - 1;
// Monitoring interval (60s)
- private static final int MONITORING_INTERNAL_MS = 60000;
+ private static final int MONITORING_INTERVAL_MS = 60000;
// Timeout once sending <Give Device Power Status>
private static final int REPORT_POWER_STATUS_TIMEOUT_MS = 5000;
@@ -142,7 +142,7 @@
mState = STATE_WAIT_FOR_REPORT_POWER_STATUS;
// Add both timers, monitoring and timeout.
- addTimer(STATE_WAIT_FOR_NEXT_MONITORING, MONITORING_INTERNAL_MS);
+ addTimer(STATE_WAIT_FOR_NEXT_MONITORING, MONITORING_INTERVAL_MS);
addTimer(STATE_WAIT_FOR_REPORT_POWER_STATUS, REPORT_POWER_STATUS_TIMEOUT_MS);
}
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 345dc21..83de0b3 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -1098,9 +1098,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/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 7d08ad0..303ab46 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -189,7 +189,8 @@
}
@NonNull
- public RoutingSessionInfo getSystemSessionInfo() {
+ public RoutingSessionInfo getSystemSessionInfo(
+ @Nullable String packageName, boolean setDeviceRouteSelected) {
final int uid = Binder.getCallingUid();
final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
final boolean hasModifyAudioRoutingPermission = mContext.checkCallingOrSelfPermission(
@@ -203,14 +204,22 @@
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
List<RoutingSessionInfo> sessionInfos;
if (hasModifyAudioRoutingPermission) {
- sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos();
- if (sessionInfos != null && !sessionInfos.isEmpty()) {
- systemSessionInfo = sessionInfos.get(0);
+ if (setDeviceRouteSelected) {
+ systemSessionInfo = userRecord.mHandler.mSystemProvider
+ .generateDeviceRouteSelectedSessionInfo(packageName);
} else {
- Slog.w(TAG, "System provider does not have any session info.");
+ sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos();
+ if (sessionInfos != null && !sessionInfos.isEmpty()) {
+ systemSessionInfo = new RoutingSessionInfo.Builder(sessionInfos.get(0))
+ .setClientPackageName(packageName).build();
+ } else {
+ Slog.w(TAG, "System provider does not have any session info.");
+ }
}
} else {
- systemSessionInfo = userRecord.mHandler.mSystemProvider.getDefaultSessionInfo();
+ systemSessionInfo = new RoutingSessionInfo.Builder(
+ userRecord.mHandler.mSystemProvider.getDefaultSessionInfo())
+ .setClientPackageName(packageName).build();
}
}
return systemSessionInfo;
@@ -403,12 +412,12 @@
////////////////////////////////////////////////////////////////
@NonNull
- public List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
+ public List<RoutingSessionInfo> getRemoteSessions(IMediaRouter2Manager manager) {
Objects.requireNonNull(manager, "manager must not be null");
final long token = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return getActiveSessionsLocked(manager);
+ return getRemoteSessionsLocked(manager);
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -861,19 +870,21 @@
//// - Should have @NonNull/@Nullable on all arguments
////////////////////////////////////////////////////////////
- private List<RoutingSessionInfo> getActiveSessionsLocked(
+ private List<RoutingSessionInfo> getRemoteSessionsLocked(
@NonNull IMediaRouter2Manager manager) {
final IBinder binder = manager.asBinder();
ManagerRecord managerRecord = mAllManagerRecords.get(binder);
if (managerRecord == null) {
- Slog.w(TAG, "getActiveSessionLocked: Ignoring unknown manager");
+ Slog.w(TAG, "getRemoteSessionLocked: Ignoring unknown manager");
return Collections.emptyList();
}
List<RoutingSessionInfo> sessionInfos = new ArrayList<>();
for (MediaRoute2Provider provider : managerRecord.mUserRecord.mHandler.mRouteProviders) {
- sessionInfos.addAll(provider.getSessionInfos());
+ if (!provider.mIsSystemRouteProvider) {
+ sessionInfos.addAll(provider.getSessionInfos());
+ }
}
return sessionInfos;
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index a57d7db..eb0b2bb 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
@@ -116,12 +117,17 @@
//TODO: remove this when it's finished
private final MediaRouter2ServiceImpl mService2;
+ private final String mDefaultAudioRouteId;
+ private final String mBluetoothA2dpRouteId;
public MediaRouterService(Context context) {
mService2 = new MediaRouter2ServiceImpl(context);
-
mContext = context;
Watchdog.getInstance().addMonitor(this);
+ Resources res = context.getResources();
+ mDefaultAudioRouteId = res.getString(com.android.internal.R.string.default_audio_route_id);
+ mBluetoothA2dpRouteId =
+ res.getString(com.android.internal.R.string.bluetooth_a2dp_audio_route_id);
mAudioService = IAudioService.Stub.asInterface(
ServiceManager.getService(Context.AUDIO_SERVICE));
@@ -451,7 +457,8 @@
// Binder call
@Override
public RoutingSessionInfo getSystemSessionInfo() {
- return mService2.getSystemSessionInfo();
+ return mService2.getSystemSessionInfo(
+ null /* packageName */, false /* setDeviceRouteSelected */);
}
// Binder call
@@ -528,8 +535,29 @@
// Binder call
@Override
- public List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager) {
- return mService2.getActiveSessions(manager);
+ public List<RoutingSessionInfo> getRemoteSessions(IMediaRouter2Manager manager) {
+ return mService2.getRemoteSessions(manager);
+ }
+
+ // Binder call
+ @Override
+ public RoutingSessionInfo getSystemSessionInfoForPackage(IMediaRouter2Manager manager,
+ String packageName) {
+ final int uid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
+ boolean setDeviceRouteSelected = false;
+ synchronized (mLock) {
+ UserRecord userRecord = mUserRecords.get(userId);
+ for (ClientRecord clientRecord : userRecord.mClientRecords) {
+ if (TextUtils.equals(clientRecord.mPackageName, packageName)) {
+ if (mDefaultAudioRouteId.equals(clientRecord.mSelectedRouteId)) {
+ setDeviceRouteSelected = true;
+ break;
+ }
+ }
+ }
+ }
+ return mService2.getSystemSessionInfo(packageName, setDeviceRouteSelected);
}
// Binder call
@@ -783,7 +811,15 @@
String routeId, boolean explicit) {
ClientRecord clientRecord = mAllClientRecords.get(client.asBinder());
if (clientRecord != null) {
- final String oldRouteId = clientRecord.mSelectedRouteId;
+ // In order not to handle system routes as a global route,
+ // set the IDs null if system routes.
+ final String oldRouteId = (mDefaultAudioRouteId.equals(clientRecord.mSelectedRouteId)
+ || mBluetoothA2dpRouteId.equals(clientRecord.mSelectedRouteId))
+ ? null : clientRecord.mSelectedRouteId;
+ clientRecord.mSelectedRouteId = routeId;
+ if (mDefaultAudioRouteId.equals(routeId) || mBluetoothA2dpRouteId.equals(routeId)) {
+ routeId = null;
+ }
if (!Objects.equals(routeId, oldRouteId)) {
if (DEBUG) {
Slog.d(TAG, clientRecord + ": Set selected route, routeId=" + routeId
@@ -791,7 +827,6 @@
+ ", explicit=" + explicit);
}
- clientRecord.mSelectedRouteId = routeId;
// Only let the system connect to new global routes for now.
// A similar check exists in the display manager for wifi display.
if (explicit && clientRecord.mTrusted) {
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 4f7af94..7878159 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -96,6 +96,9 @@
mHandler.post(() -> {
updateDeviceRoute(newRoutes);
notifyProviderState();
+ if (updateSessionInfosIfNeeded()) {
+ notifySessionInfoUpdated();
+ }
});
}
};
@@ -120,10 +123,7 @@
// .getInstance returns null if there is no bt adapter available
mBtRouteProvider = BluetoothRouteProvider.createInstance(context, (routes) -> {
publishProviderState();
-
- boolean sessionInfoChanged;
- sessionInfoChanged = updateSessionInfosIfNeeded();
- if (sessionInfoChanged) {
+ if (updateSessionInfosIfNeeded()) {
notifySessionInfoUpdated();
}
});
@@ -237,6 +237,23 @@
return mDefaultSessionInfo;
}
+ public RoutingSessionInfo generateDeviceRouteSelectedSessionInfo(String packageName) {
+ synchronized (mLock) {
+ if (mSessionInfos.isEmpty()) {
+ return null;
+ }
+ RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
+ SYSTEM_SESSION_ID, packageName).setSystemSession(true);
+ builder.addSelectedRoute(mDeviceRoute.getId());
+ if (mBtRouteProvider != null) {
+ for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
+ builder.addTransferableRoute(route.getId());
+ }
+ }
+ return builder.setProviderId(mUniqueId).build();
+ }
+ }
+
private void updateDeviceRoute(AudioRoutesInfo newRoutes) {
int name = R.string.default_audio_route_name;
int type = TYPE_BUILTIN_SPEAKER;
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index db0f1eb..99ba19d 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -345,7 +345,7 @@
private static class FeatureConfigImpl implements FeatureConfig, CompatChange.ChangeListener {
private static final String FILTERING_ENABLED_NAME = "package_query_filtering_enabled";
- private final PackageManagerService.Injector mInjector;
+ private final PackageManagerServiceInjector mInjector;
private final PackageManagerInternal mPmInternal;
private volatile boolean mFeatureEnabled =
PackageManager.APP_ENUMERATION_ENABLED_BY_DEFAULT;
@@ -356,7 +356,7 @@
private AppsFilter mAppsFilter;
private FeatureConfigImpl(
- PackageManagerInternal pmInternal, PackageManagerService.Injector injector) {
+ PackageManagerInternal pmInternal, PackageManagerServiceInjector injector) {
mPmInternal = pmInternal;
mInjector = injector;
}
@@ -465,20 +465,20 @@
@Override
public void updatePackageState(PackageSetting setting, boolean removed) {
- final boolean enableLogging = setting.pkg != null &&
- !removed && (setting.pkg.isTestOnly() || setting.pkg.isDebuggable());
- enableLogging(setting.appId, enableLogging);
+ final boolean enableLogging = setting.getPkg() != null &&
+ !removed && (setting.getPkg().isTestOnly() || setting.getPkg().isDebuggable());
+ enableLogging(setting.getAppId(), enableLogging);
if (removed) {
- mDisabledPackages.remove(setting.name);
- } else if (setting.pkg != null) {
- updateEnabledState(setting.pkg);
+ mDisabledPackages.remove(setting.getPackageName());
+ } else if (setting.getPkg() != null) {
+ updateEnabledState(setting.getPkg());
}
}
}
/** Builder method for an AppsFilter */
public static AppsFilter create(
- PackageManagerInternal pms, PackageManagerService.Injector injector) {
+ PackageManagerInternal pms, PackageManagerServiceInjector injector) {
final boolean forceSystemAppsQueryable =
injector.getContext().getResources()
.getBoolean(R.bool.config_forceSystemPackagesQueryable);
@@ -537,7 +537,7 @@
private static boolean canQueryAsInstaller(PackageSetting querying,
AndroidPackage potentialTarget) {
- final InstallSource installSource = querying.installSource;
+ final InstallSource installSource = querying.getInstallSource();
if (potentialTarget.getPackageName().equals(installSource.installerPackageName)) {
return true;
}
@@ -735,19 +735,19 @@
@Nullable
private ArraySet<String> addPackageInternal(PackageSetting newPkgSetting,
ArrayMap<String, PackageSetting> existingSettings) {
- if (Objects.equals("android", newPkgSetting.name)) {
+ if (Objects.equals("android", newPkgSetting.getPackageName())) {
// let's set aside the framework signatures
- mSystemSigningDetails = newPkgSetting.signatures.mSigningDetails;
+ mSystemSigningDetails = newPkgSetting.getSigningDetails();
// and since we add overlays before we add the framework, let's revisit already added
// packages for signature matches
for (PackageSetting setting : existingSettings.values()) {
if (isSystemSigned(mSystemSigningDetails, setting)) {
- mForceQueryable.add(setting.appId);
+ mForceQueryable.add(setting.getAppId());
}
}
}
- final AndroidPackage newPkg = newPkgSetting.pkg;
+ final AndroidPackage newPkg = newPkgSetting.getPkg();
if (newPkg == null) {
return null;
}
@@ -757,9 +757,9 @@
}
final boolean newIsForceQueryable =
- mForceQueryable.contains(newPkgSetting.appId)
+ mForceQueryable.contains(newPkgSetting.getAppId())
/* shared user that is already force queryable */
- || newPkgSetting.forceQueryableOverride /* adb override */
+ || newPkgSetting.isForceQueryableOverride() /* adb override */
|| (newPkgSetting.isSystem() && (mSystemAppsQueryable
|| newPkg.isForceQueryable()
|| ArrayUtils.contains(mForceQueryableByDevicePackageNames,
@@ -767,49 +767,52 @@
if (newIsForceQueryable
|| (mSystemSigningDetails != null
&& isSystemSigned(mSystemSigningDetails, newPkgSetting))) {
- mForceQueryable.add(newPkgSetting.appId);
+ mForceQueryable.add(newPkgSetting.getAppId());
}
for (int i = existingSettings.size() - 1; i >= 0; i--) {
final PackageSetting existingSetting = existingSettings.valueAt(i);
- if (existingSetting.appId == newPkgSetting.appId || existingSetting.pkg == null) {
+ if (existingSetting.getAppId() == newPkgSetting.getAppId() || existingSetting.getPkg()
+ == null) {
continue;
}
- final AndroidPackage existingPkg = existingSetting.pkg;
+ final AndroidPackage existingPkg = existingSetting.getPkg();
// let's evaluate the ability of already added packages to see this new package
if (!newIsForceQueryable) {
if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(existingPkg,
newPkg, mProtectedBroadcasts)) {
- mQueriesViaComponent.add(existingSetting.appId, newPkgSetting.appId);
+ mQueriesViaComponent.add(existingSetting.getAppId(), newPkgSetting.getAppId());
}
if (canQueryViaPackage(existingPkg, newPkg)
|| canQueryAsInstaller(existingSetting, newPkg)) {
- mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
+ mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId());
}
if (canQueryViaUsesLibrary(existingPkg, newPkg)) {
- mQueryableViaUsesLibrary.add(existingSetting.appId, newPkgSetting.appId);
+ mQueryableViaUsesLibrary.add(existingSetting.getAppId(),
+ newPkgSetting.getAppId());
}
}
// now we'll evaluate our new package's ability to see existing packages
- if (!mForceQueryable.contains(existingSetting.appId)) {
+ if (!mForceQueryable.contains(existingSetting.getAppId())) {
if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(newPkg,
existingPkg, mProtectedBroadcasts)) {
- mQueriesViaComponent.add(newPkgSetting.appId, existingSetting.appId);
+ mQueriesViaComponent.add(newPkgSetting.getAppId(), existingSetting.getAppId());
}
if (canQueryViaPackage(newPkg, existingPkg)
|| canQueryAsInstaller(newPkgSetting, existingPkg)) {
- mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
+ mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId());
}
if (canQueryViaUsesLibrary(newPkg, existingPkg)) {
- mQueryableViaUsesLibrary.add(newPkgSetting.appId, existingSetting.appId);
+ mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(),
+ existingSetting.getAppId());
}
}
// if either package instruments the other, mark both as visible to one another
- if (newPkgSetting.pkg != null && existingSetting.pkg != null
- && (pkgInstruments(newPkgSetting.pkg, existingSetting.pkg)
- || pkgInstruments(existingSetting.pkg, newPkgSetting.pkg))) {
- mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
- mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
+ if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null
+ && (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg())
+ || pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) {
+ mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId());
+ mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId());
}
}
@@ -817,13 +820,13 @@
ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize);
for (int index = 0; index < existingSize; index++) {
PackageSetting pkgSetting = existingSettings.valueAt(index);
- if (pkgSetting.pkg != null) {
- existingPkgs.put(pkgSetting.name, pkgSetting.pkg);
+ if (pkgSetting.getPkg() != null) {
+ existingPkgs.put(pkgSetting.getPackageName(), pkgSetting.getPkg());
}
}
ArraySet<String> changedPackages =
- mOverlayReferenceMapper.addPkg(newPkgSetting.pkg, existingPkgs);
+ mOverlayReferenceMapper.addPkg(newPkgSetting.getPkg(), existingPkgs);
mFeatureConfig.updatePackageState(newPkgSetting, false /*removed*/);
@@ -880,7 +883,7 @@
// store away the references to the immutable packages, since settings are retained
// during updates.
for (int i = 0, max = settings.size(); i < max; i++) {
- final AndroidPackage pkg = settings.valueAt(i).pkg;
+ final AndroidPackage pkg = settings.valueAt(i).getPkg();
packagesCache.put(settings.keyAt(i), pkg);
}
});
@@ -894,7 +897,7 @@
return;
}
for (int i = 0, max = settings.size(); i < max; i++) {
- final AndroidPackage pkg = settings.valueAt(i).pkg;
+ final AndroidPackage pkg = settings.valueAt(i).getPkg();
if (!Objects.equals(pkg, packagesCache.get(settings.keyAt(i)))) {
changed[0] = true;
return;
@@ -942,11 +945,12 @@
PackageSetting> allSettings, UserInfo[] allUsers, int maxIndex) {
for (int i = Math.min(maxIndex, allSettings.size() - 1); i >= 0; i--) {
PackageSetting otherSetting = allSettings.valueAt(i);
- if (subjectSetting.appId == otherSetting.appId) {
+ if (subjectSetting.getAppId() == otherSetting.getAppId()) {
continue;
}
//noinspection StringEquality
- if (subjectSetting.name == skipPackageName || otherSetting.name == skipPackageName) {
+ if (subjectSetting.getPackageName() == skipPackageName || otherSetting.getPackageName()
+ == skipPackageName) {
continue;
}
final int userCount = allUsers.length;
@@ -955,8 +959,8 @@
int subjectUser = allUsers[su].id;
for (int ou = 0; ou < userCount; ou++) {
int otherUser = allUsers[ou].id;
- int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.appId);
- int otherUid = UserHandle.getUid(otherUser, otherSetting.appId);
+ int subjectUid = UserHandle.getUid(subjectUser, subjectSetting.getAppId());
+ int otherUid = UserHandle.getUid(otherUser, otherSetting.getAppId());
cache.put(subjectUid, otherUid,
shouldFilterApplicationInternal(
subjectUid, subjectSetting, otherSetting, otherUser));
@@ -971,7 +975,7 @@
private static boolean isSystemSigned(@NonNull SigningDetails sysSigningDetails,
PackageSetting pkgSetting) {
return pkgSetting.isSystem()
- && pkgSetting.signatures.mSigningDetails.signaturesMatchExactly(sysSigningDetails);
+ && pkgSetting.getSigningDetails().signaturesMatchExactly(sysSigningDetails);
}
private ArraySet<String> collectProtectedBroadcasts(
@@ -979,10 +983,11 @@
ArraySet<String> ret = new ArraySet<>();
for (int i = existingSettings.size() - 1; i >= 0; i--) {
PackageSetting setting = existingSettings.valueAt(i);
- if (setting.pkg == null || setting.pkg.getPackageName().equals(excludePackage)) {
+ if (setting.getPkg() == null || setting.getPkg().getPackageName().equals(
+ excludePackage)) {
continue;
}
- final List<String> protectedBroadcasts = setting.pkg.getProtectedBroadcasts();
+ final List<String> protectedBroadcasts = setting.getPkg().getProtectedBroadcasts();
if (!protectedBroadcasts.isEmpty()) {
ret.addAll(protectedBroadcasts);
}
@@ -999,7 +1004,7 @@
mQueriesViaComponent.clear();
for (int i = existingSettings.size() - 1; i >= 0; i--) {
PackageSetting setting = existingSettings.valueAt(i);
- if (setting.pkg == null || requestsQueryAllPackages(setting.pkg)) {
+ if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) {
continue;
}
for (int j = existingSettings.size() - 1; j >= 0; j--) {
@@ -1007,11 +1012,13 @@
continue;
}
final PackageSetting otherSetting = existingSettings.valueAt(j);
- if (otherSetting.pkg == null || mForceQueryable.contains(otherSetting.appId)) {
+ if (otherSetting.getPkg() == null || mForceQueryable.contains(
+ otherSetting.getAppId())) {
continue;
}
- if (canQueryViaComponents(setting.pkg, otherSetting.pkg, mProtectedBroadcasts)) {
- mQueriesViaComponent.add(setting.appId, otherSetting.appId);
+ if (canQueryViaComponents(setting.getPkg(), otherSetting.getPkg(),
+ mProtectedBroadcasts)) {
+ mQueriesViaComponent.add(setting.getAppId(), otherSetting.getAppId());
}
}
}
@@ -1035,7 +1042,7 @@
@Nullable
public SparseArray<int[]> getVisibilityAllowList(PackageSetting setting, int[] users,
ArrayMap<String, PackageSetting> existingSettings) {
- if (mForceQueryable.contains(setting.appId)) {
+ if (mForceQueryable.contains(setting.getAppId())) {
return null;
}
// let's reserve max memory to limit the number of allocations
@@ -1047,7 +1054,7 @@
int allowListSize = 0;
for (int i = existingSettings.size() - 1; i >= 0; i--) {
final PackageSetting existingSetting = existingSettings.valueAt(i);
- final int existingAppId = existingSetting.appId;
+ final int existingAppId = existingSetting.getAppId();
if (existingAppId < Process.FIRST_APPLICATION_UID) {
continue;
}
@@ -1102,7 +1109,7 @@
final int userCount = users.length;
for (int u = 0; u < userCount; u++) {
final int userId = users[u].id;
- final int removingUid = UserHandle.getUid(userId, setting.appId);
+ final int removingUid = UserHandle.getUid(userId, setting.getAppId());
mImplicitlyQueryable.remove(removingUid);
for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) {
mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), removingUid);
@@ -1120,24 +1127,25 @@
}
if (!mQueriesViaComponentRequireRecompute) {
- mQueriesViaComponent.remove(setting.appId);
+ mQueriesViaComponent.remove(setting.getAppId());
for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) {
- mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.appId);
+ mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.getAppId());
}
}
- mQueriesViaPackage.remove(setting.appId);
+ mQueriesViaPackage.remove(setting.getAppId());
for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
- mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId);
+ mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.getAppId());
}
- mQueryableViaUsesLibrary.remove(setting.appId);
+ mQueryableViaUsesLibrary.remove(setting.getAppId());
for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) {
- mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), setting.appId);
+ mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i),
+ setting.getAppId());
}
- mForceQueryable.remove(setting.appId);
+ mForceQueryable.remove(setting.getAppId());
- if (setting.pkg != null && !setting.pkg.getProtectedBroadcasts().isEmpty()) {
- final String removingPackageName = setting.pkg.getPackageName();
+ if (setting.getPkg() != null && !setting.getPkg().getProtectedBroadcasts().isEmpty()) {
+ final String removingPackageName = setting.getPkg().getPackageName();
final Set<String> protectedBroadcasts = mProtectedBroadcasts;
mProtectedBroadcasts = collectProtectedBroadcasts(settings, removingPackageName);
if (!mProtectedBroadcasts.containsAll(protectedBroadcasts)) {
@@ -1146,7 +1154,7 @@
}
ArraySet<String> additionalChangedPackages =
- mOverlayReferenceMapper.removePkg(setting.name);
+ mOverlayReferenceMapper.removePkg(setting.getPackageName());
mFeatureConfig.updatePackageState(setting, true /*removed*/);
@@ -1154,25 +1162,26 @@
// shared user members to re-establish visibility between them and other packages.
// NOTE: this must come after all removals from data structures but before we update the
// cache
- if (setting.sharedUser != null) {
- for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) {
- if (setting.sharedUser.packages.valueAt(i) == setting) {
+ if (setting.getSharedUser() != null) {
+ for (int i = setting.getSharedUser().packages.size() - 1; i >= 0; i--) {
+ if (setting.getSharedUser().packages.valueAt(i) == setting) {
continue;
}
addPackageInternal(
- setting.sharedUser.packages.valueAt(i), settings);
+ setting.getSharedUser().packages.valueAt(i), settings);
}
}
synchronized (mCacheLock) {
- removeAppIdFromVisibilityCache(setting.appId);
- if (mShouldFilterCache != null && setting.sharedUser != null) {
- for (int i = setting.sharedUser.packages.size() - 1; i >= 0; i--) {
- PackageSetting siblingSetting = setting.sharedUser.packages.valueAt(i);
+ removeAppIdFromVisibilityCache(setting.getAppId());
+ if (mShouldFilterCache != null && setting.getSharedUser() != null) {
+ for (int i = setting.getSharedUser().packages.size() - 1; i >= 0; i--) {
+ PackageSetting siblingSetting = setting.getSharedUser().packages.valueAt(i);
if (siblingSetting == setting) {
continue;
}
- updateShouldFilterCacheForPackage(mShouldFilterCache, setting.name,
+ updateShouldFilterCacheForPackage(mShouldFilterCache,
+ setting.getPackageName(),
siblingSetting, settings, users, settings.size());
}
}
@@ -1218,8 +1227,8 @@
try {
int callingAppId = UserHandle.getAppId(callingUid);
if (callingAppId < Process.FIRST_APPLICATION_UID
- || targetPkgSetting.appId < Process.FIRST_APPLICATION_UID
- || callingAppId == targetPkgSetting.appId) {
+ || targetPkgSetting.getAppId() < Process.FIRST_APPLICATION_UID
+ || callingAppId == targetPkgSetting.getAppId()) {
return false;
}
synchronized (mCacheLock) {
@@ -1230,7 +1239,7 @@
+ callingUid);
return true;
}
- final int targetUid = UserHandle.getUid(userId, targetPkgSetting.appId);
+ final int targetUid = UserHandle.getUid(userId, targetPkgSetting.getAppId());
final int targetIndex = mShouldFilterCache.indexOfKey(targetUid);
if (targetIndex < 0) {
Slog.w(TAG, "Encountered calling -> target with no cached rules: "
@@ -1279,13 +1288,13 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof");
}
if (callingSetting instanceof PackageSetting) {
- if (((PackageSetting) callingSetting).sharedUser == null) {
+ if (((PackageSetting) callingSetting).getSharedUser() == null) {
callingPkgSetting = (PackageSetting) callingSetting;
callingSharedPkgSettings = null;
} else {
callingPkgSetting = null;
callingSharedPkgSettings =
- ((PackageSetting) callingSetting).sharedUser.packages;
+ ((PackageSetting) callingSetting).getSharedUser().packages;
}
} else {
callingPkgSetting = null;
@@ -1296,8 +1305,8 @@
}
if (callingPkgSetting != null) {
- if (callingPkgSetting.pkg != null
- && !mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) {
+ if (callingPkgSetting.getPkg() != null
+ && !mFeatureConfig.packageIsEnabled(callingPkgSetting.getPkg())) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "DISABLED");
}
@@ -1305,7 +1314,7 @@
}
} else {
for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) {
- final AndroidPackage pkg = callingSharedPkgSettings.valueAt(i).pkg;
+ final AndroidPackage pkg = callingSharedPkgSettings.valueAt(i).getPkg();
if (pkg != null && !mFeatureConfig.packageIsEnabled(pkg)) {
if (DEBUG_LOGGING) {
log(callingSetting, targetPkgSetting, "DISABLED");
@@ -1320,11 +1329,12 @@
}
final int callingAppId;
if (callingPkgSetting != null) {
- callingAppId = callingPkgSetting.appId;
+ callingAppId = callingPkgSetting.getAppId();
} else {
- callingAppId = callingSharedPkgSettings.valueAt(0).appId; // all should be the same
+ // all should be the same
+ callingAppId = callingSharedPkgSettings.valueAt(0).getAppId();
}
- final int targetAppId = targetPkgSetting.appId;
+ final int targetAppId = targetPkgSetting.getAppId();
if (DEBUG_TRACING) {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -1340,13 +1350,13 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "requestsQueryAllPackages");
}
if (callingPkgSetting != null) {
- if (callingPkgSetting.pkg != null
- && requestsQueryAllPackages(callingPkgSetting.pkg)) {
+ if (callingPkgSetting.getPkg() != null
+ && requestsQueryAllPackages(callingPkgSetting.getPkg())) {
return false;
}
} else {
for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) {
- AndroidPackage pkg = callingSharedPkgSettings.valueAt(i).pkg;
+ AndroidPackage pkg = callingSharedPkgSettings.valueAt(i).getPkg();
if (pkg != null && requestsQueryAllPackages(pkg)) {
return false;
}
@@ -1360,7 +1370,7 @@
// This package isn't technically installed and won't be written to settings, so we can
// treat it as filtered until it's available again.
- final AndroidPackage targetPkg = targetPkgSetting.pkg;
+ final AndroidPackage targetPkg = targetPkgSetting.getPkg();
if (targetPkg == null) {
if (DEBUG_LOGGING) {
Slog.wtf(TAG, "shouldFilterApplication: " + "targetPkg is null");
@@ -1467,7 +1477,8 @@
int size = callingSharedPkgSettings.size();
for (int index = 0; index < size; index++) {
PackageSetting pkgSetting = callingSharedPkgSettings.valueAt(index);
- if (mOverlayReferenceMapper.isValidActor(targetName, pkgSetting.name)) {
+ if (mOverlayReferenceMapper.isValidActor(targetName,
+ pkgSetting.getPackageName())) {
if (DEBUG_LOGGING) {
log(callingPkgSetting, targetPkgSetting,
"matches shared user of package that acts on target of "
@@ -1477,7 +1488,8 @@
}
}
} else {
- if (mOverlayReferenceMapper.isValidActor(targetName, callingPkgSetting.name)) {
+ if (mOverlayReferenceMapper.isValidActor(targetName,
+ callingPkgSetting.getPackageName())) {
if (DEBUG_LOGGING) {
log(callingPkgSetting, targetPkgSetting, "acts on target of overlay");
}
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
new file mode 100644
index 0000000..4bcb471
--- /dev/null
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -0,0 +1,338 @@
+/*
+ * 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.pm;
+
+import static android.os.PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED;
+import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
+
+import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
+import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
+import static com.android.server.pm.PackageManagerService.PACKAGE_SCHEME;
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+import static com.android.server.pm.PackageManagerService.TAG;
+
+import android.Manifest;
+import android.annotation.AppIdInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.BroadcastOptions;
+import android.app.IActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.IIntentReceiver;
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.PowerExemptionManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Helper class to send broadcasts for various situations.
+ */
+public final class BroadcastHelper {
+ private static final boolean DEBUG_BROADCASTS = false;
+ /**
+ * Permissions required in order to receive instant application lifecycle broadcasts.
+ */
+ private static final String[] INSTANT_APP_BROADCAST_PERMISSION =
+ new String[]{android.Manifest.permission.ACCESS_INSTANT_APPS};
+
+ private final UserManagerInternal mUmInternal;
+ private final ActivityManagerInternal mAmInternal;
+ private final Context mContext;
+
+ BroadcastHelper(PackageManagerServiceInjector injector) {
+ mUmInternal = injector.getUserManagerInternal();
+ mAmInternal = injector.getActivityManagerInternal();
+ mContext = injector.getContext();
+ }
+
+ public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
+ final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
+ final int[] userIds, int[] instantUserIds,
+ @Nullable SparseArray<int[]> broadcastAllowList,
+ @Nullable Bundle bOptions) {
+ try {
+ final IActivityManager am = ActivityManager.getService();
+ if (am == null) return;
+ final int[] resolvedUserIds;
+ if (userIds == null) {
+ resolvedUserIds = am.getRunningUserIds();
+ } else {
+ resolvedUserIds = userIds;
+ }
+ doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
+ resolvedUserIds, false, broadcastAllowList, bOptions);
+ if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
+ doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
+ instantUserIds, true, null, bOptions);
+ }
+ } catch (RemoteException ex) {
+ }
+ }
+
+ /**
+ * Sends a broadcast for the given action.
+ * <p>If {@code isInstantApp} is {@code true}, then the broadcast is protected with
+ * the {@link android.Manifest.permission#ACCESS_INSTANT_APPS} permission. This allows
+ * the system and applications allowed to see instant applications to receive package
+ * lifecycle events for instant applications.
+ */
+ public void doSendBroadcast(String action, String pkg, Bundle extras,
+ int flags, String targetPkg, IIntentReceiver finishedReceiver,
+ int[] userIds, boolean isInstantApp, @Nullable SparseArray<int[]> broadcastAllowList,
+ @Nullable Bundle bOptions) {
+ for (int id : userIds) {
+ final Intent intent = new Intent(action,
+ pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
+ final String[] requiredPermissions =
+ isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null;
+ if (extras != null) {
+ intent.putExtras(extras);
+ }
+ if (targetPkg != null) {
+ intent.setPackage(targetPkg);
+ }
+ // Modify the UID when posting to other users
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ if (uid > 0 && UserHandle.getUserId(uid) != id) {
+ uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ }
+ if (broadcastAllowList != null && PLATFORM_PACKAGE_NAME.equals(targetPkg)) {
+ intent.putExtra(Intent.EXTRA_VISIBILITY_ALLOW_LIST, broadcastAllowList.get(id));
+ }
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, id);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
+ if (DEBUG_BROADCASTS) {
+ RuntimeException here = new RuntimeException("here");
+ here.fillInStackTrace();
+ Slog.d(TAG, "Sending to user " + id + ": "
+ + intent.toShortString(false, true, false, false)
+ + " " + intent.getExtras(), here);
+ }
+ mAmInternal.broadcastIntent(
+ intent, finishedReceiver, requiredPermissions,
+ finishedReceiver != null, id,
+ broadcastAllowList == null ? null : broadcastAllowList.get(id),
+ bOptions);
+ }
+ }
+
+ public void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+ ArrayList<String> pkgList, int[] uidArr, IIntentReceiver finishedReceiver) {
+ sendResourcesChangedBroadcast(mediaStatus, replacing,
+ pkgList.toArray(new String[pkgList.size()]), uidArr, finishedReceiver);
+ }
+
+ public void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+ String[] pkgList, int[] uidArr, IIntentReceiver finishedReceiver) {
+ int size = pkgList.length;
+ if (size > 0) {
+ // Send broadcasts here
+ Bundle extras = new Bundle();
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+ if (uidArr != null) {
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
+ }
+ if (replacing) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, replacing);
+ }
+ String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
+ : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
+ // TODO: not sure how to handle this one.
+ sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver,
+ null, null, null, null);
+ }
+ }
+
+ /**
+ * The just-installed/enabled app is bundled on the system, so presumed to be able to run
+ * automatically without needing an explicit launch.
+ * Send it a LOCKED_BOOT_COMPLETED/BOOT_COMPLETED if it would ordinarily have gotten ones.
+ */
+ public void sendBootCompletedBroadcastToSystemApp(
+ String packageName, boolean includeStopped, int userId) {
+ // If user is not running, the app didn't miss any broadcast
+ if (!mUmInternal.isUserRunning(userId)) {
+ return;
+ }
+ final IActivityManager am = ActivityManager.getService();
+ try {
+ // Deliver LOCKED_BOOT_COMPLETED first
+ Intent lockedBcIntent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
+ .setPackage(packageName);
+ if (includeStopped) {
+ lockedBcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ }
+ final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED};
+ final BroadcastOptions bOptions = getTemporaryAppAllowlistBroadcastOptions(
+ REASON_LOCKED_BOOT_COMPLETED);
+ am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null,
+ requiredPermissions, null, android.app.AppOpsManager.OP_NONE,
+ bOptions.toBundle(), false, false, userId);
+
+ // Deliver BOOT_COMPLETED only if user is unlocked
+ if (mUmInternal.isUserUnlockingOrUnlocked(userId)) {
+ Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED).setPackage(packageName);
+ if (includeStopped) {
+ bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+ }
+ am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null,
+ requiredPermissions, null, android.app.AppOpsManager.OP_NONE,
+ bOptions.toBundle(), false, false, userId);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ public @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
+ @PowerExemptionManager.ReasonCode int reasonCode) {
+ long duration = 10_000;
+ if (mAmInternal != null) {
+ duration = mAmInternal.getBootTimeTempAllowListDuration();
+ }
+ final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
+ bOptions.setTemporaryAppAllowlist(duration,
+ TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ reasonCode, "");
+ return bOptions;
+ }
+
+ public void sendPackageChangedBroadcast(String packageName, boolean dontKillApp,
+ ArrayList<String> componentNames, int packageUid, String reason,
+ int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList) {
+ if (DEBUG_INSTALL) {
+ Log.v(TAG, "Sending package changed: package=" + packageName + " components="
+ + componentNames);
+ }
+ Bundle extras = new Bundle(4);
+ extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
+ String[] nameList = new String[componentNames.size()];
+ componentNames.toArray(nameList);
+ extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
+ extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, dontKillApp);
+ extras.putInt(Intent.EXTRA_UID, packageUid);
+ if (reason != null) {
+ extras.putString(Intent.EXTRA_REASON, reason);
+ }
+ // If this is not reporting a change of the overall package, then only send it
+ // to registered receivers. We don't want to launch a swath of apps for every
+ // little component state change.
+ final int flags = !componentNames.contains(packageName)
+ ? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0;
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null,
+ userIds, instantUserIds, broadcastAllowList, null);
+ }
+
+ public static void sendDeviceCustomizationReadyBroadcast() {
+ final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY);
+ intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ final IActivityManager am = ActivityManager.getService();
+ final String[] requiredPermissions = {
+ Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY,
+ };
+ try {
+ am.broadcastIntentWithFeature(null, null, intent, null, null, 0, null, null,
+ requiredPermissions, null, android.app.AppOpsManager.OP_NONE, null, false,
+ false, UserHandle.USER_ALL);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId,
+ int launcherUid, @Nullable ComponentName launcherComponent,
+ @Nullable String appPredictionServicePackage) {
+ if (launcherComponent != null) {
+ Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
+ .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+ .setPackage(launcherComponent.getPackageName());
+ mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid));
+ }
+ // TODO(b/122900055) Change/Remove this and replace with new permission role.
+ if (appPredictionServicePackage != null) {
+ Intent predictorIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
+ .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
+ .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
+ .setPackage(appPredictionServicePackage);
+ mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUid));
+ }
+ }
+
+ public void sendPreferredActivityChangedBroadcast(int userId) {
+ final IActivityManager am = ActivityManager.getService();
+ if (am == null) {
+ return;
+ }
+
+ final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ try {
+ am.broadcastIntentWithFeature(null, null, intent, null, null,
+ 0, null, null, null, null, android.app.AppOpsManager.OP_NONE,
+ null, false, false, userId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void sendPackageAddedForNewUsers(String packageName,
+ @AppIdInt int appId, int[] userIds, int[] instantUserIds,
+ int dataLoaderType, SparseArray<int[]> broadcastAllowlist) {
+ Bundle extras = new Bundle(1);
+ // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
+ final int uid = UserHandle.getUid(
+ (ArrayUtils.isEmpty(userIds) ? instantUserIds[0] : userIds[0]), appId);
+ extras.putInt(Intent.EXTRA_UID, uid);
+ extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
+
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+ packageName, extras, 0, null, null, userIds, instantUserIds,
+ broadcastAllowlist, null);
+ }
+
+ public void sendDistractingPackagesChanged(String[] pkgList, int[] uidList, int userId,
+ int distractionFlags) {
+ final Bundle extras = new Bundle(3);
+ extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
+ extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
+ extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
+ sendPackageBroadcast(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null, extras,
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, new int[]{userId}, null, null,
+ null);
+ }
+
+ public void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
+ int[] userIds, int[] instantUserIds) {
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
+ installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */, null);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java
index e0f11e2..75dbf04 100644
--- a/services/core/java/com/android/server/pm/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/ComponentResolver.java
@@ -542,7 +542,7 @@
final PackageSetting disabledPkgSetting = (PackageSetting) sPackageManagerInternal
.getDisabledSystemPackage(pair.first.getPackageName());
final AndroidPackage disabledPkg =
- disabledPkgSetting == null ? null : disabledPkgSetting.pkg;
+ disabledPkgSetting == null ? null : disabledPkgSetting.getPkg();
final List<ParsedActivity> systemActivities =
disabledPkg != null ? disabledPkg.getActivities() : null;
adjustPriority(systemActivities, pair.first, pair.second, setupWizardPackage);
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index ff4a8fd..9f98cd8 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -170,7 +170,7 @@
private final UserManagerService mUserManager;
private final PermissionManagerServiceInternal mPermissionManager;
private final ApexManager mApexManager;
- private final PackageManagerService.Injector mInjector;
+ private final PackageManagerServiceInjector mInjector;
private final ComponentResolver mComponentResolver;
private final InstantAppResolverConnection mInstantAppResolverConnection;
private final DefaultAppProvider mDefaultAppProvider;
@@ -556,10 +556,10 @@
final PackageSetting setting =
getPackageSettingInternal(pkgName, Process.SYSTEM_UID);
result = null;
- if (setting != null && setting.pkg != null && (resolveForStart
+ if (setting != null && setting.getPkg() != null && (resolveForStart
|| !shouldFilterApplicationLocked(setting, filterCallingUid, userId))) {
result = filterIfNotSystemUser(mComponentResolver.queryActivities(
- intent, resolvedType, flags, setting.pkg.getActivities(), userId),
+ intent, resolvedType, flags, setting.getPkg().getActivities(), userId),
userId);
}
if (result == null || result.size() == 0) {
@@ -679,17 +679,17 @@
if (shouldFilterApplicationLocked(ps, filterCallingUid, userId)) {
return null;
}
- if (ps.pkg == null) {
+ if (ps.getPkg() == null) {
final PackageInfo pInfo = generatePackageInfo(ps, flags, userId);
if (pInfo != null) {
return pInfo.applicationInfo;
}
return null;
}
- ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(ps.pkg, flags,
+ ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(ps.getPkg(), flags,
ps.readUserState(userId), userId, ps);
if (ai != null) {
- ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
+ ai.packageName = resolveExternalPackageNameLPr(ps.getPkg());
}
return ai;
}
@@ -1366,18 +1366,19 @@
}
final PackageUserState state = ps.readUserState(userId);
- AndroidPackage p = ps.pkg;
+ AndroidPackage p = ps.getPkg();
if (p != null) {
// Compute GIDs only if requested
final int[] gids = (flags & PackageManager.GET_GIDS) == 0 ? EMPTY_INT_ARRAY
- : mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
+ : mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.getAppId()));
// Compute granted permissions only if package has requested permissions
final Set<String> permissions = ((flags & PackageManager.GET_PERMISSIONS) == 0
|| ArrayUtils.isEmpty(p.getRequestedPermissions())) ? Collections.emptySet()
- : mPermissionManager.getGrantedPermissions(ps.name, userId);
+ : mPermissionManager.getGrantedPermissions(ps.getPackageName(), userId);
PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags,
- ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId, ps);
+ ps.getFirstInstallTime(), ps.getLastUpdateTime(), permissions, state, userId,
+ ps);
if (packageInfo == null) {
return null;
@@ -1389,18 +1390,18 @@
return packageInfo;
} else if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0 && state.isAvailable(flags)) {
PackageInfo pi = new PackageInfo();
- pi.packageName = ps.name;
- pi.setLongVersionCode(ps.versionCode);
- pi.sharedUserId = (ps.sharedUser != null) ? ps.sharedUser.name : null;
- pi.firstInstallTime = ps.firstInstallTime;
- pi.lastUpdateTime = ps.lastUpdateTime;
+ pi.packageName = ps.getPackageName();
+ pi.setLongVersionCode(ps.getVersionCode());
+ pi.sharedUserId = (ps.getSharedUser() != null) ? ps.getSharedUser().name : null;
+ pi.firstInstallTime = ps.getFirstInstallTime();
+ pi.lastUpdateTime = ps.getLastUpdateTime();
ApplicationInfo ai = new ApplicationInfo();
- ai.packageName = ps.name;
- ai.uid = UserHandle.getUid(userId, ps.appId);
- ai.primaryCpuAbi = ps.primaryCpuAbiString;
- ai.secondaryCpuAbi = ps.secondaryCpuAbiString;
- ai.setVersionCode(ps.versionCode);
+ ai.packageName = ps.getPackageName();
+ ai.uid = UserHandle.getUid(userId, ps.getAppId());
+ ai.primaryCpuAbi = ps.getPrimaryCpuAbi();
+ ai.secondaryCpuAbi = ps.getSecondaryCpuAbi();
+ ai.setVersionCode(ps.getVersionCode());
ai.flags = ps.pkgFlags;
ai.privateFlags = ps.pkgPrivateFlags;
pi.applicationInfo = PackageInfoWithoutStateUtils.generateDelegateApplicationInfo(
@@ -1408,7 +1409,7 @@
if (DEBUG_PACKAGE_INFO) {
Log.v(TAG, "ps.pkg is n/a for ["
- + ps.name + "]. Provides a minimum info.");
+ + ps.getPackageName() + "]. Provides a minimum info.");
}
return pi;
} else {
@@ -1824,7 +1825,7 @@
if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
final boolean isInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid));
- return isInstantApp ? ps.pkg.getPackageName() : null;
+ return isInstantApp ? ps.getPkg().getPackageName() : null;
}
return null;
}
@@ -1964,7 +1965,7 @@
final PackageSetting ps = sus.packages.valueAt(index);
if (ps.getInstalled(userId)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
- res[i++] = ps.name;
+ res[i++] = ps.getPackageName();
}
}
return ArrayUtils.trimToSize(res, i);
@@ -1972,7 +1973,7 @@
final PackageSetting ps = (PackageSetting) obj;
if (ps.getInstalled(userId)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
- return new String[]{ps.name};
+ return new String[]{ps.getPackageName()};
}
}
return null;
@@ -2049,12 +2050,12 @@
}
// No package means no static lib as it is always on internal storage
- if (ps == null || ps.pkg == null || !ps.pkg.isStaticSharedLibrary()) {
+ if (ps == null || ps.getPkg() == null || !ps.getPkg().isStaticSharedLibrary()) {
return false;
}
final SharedLibraryInfo libraryInfo = getSharedLibraryInfoLPr(
- ps.pkg.getStaticSharedLibName(), ps.pkg.getStaticSharedLibVersion());
+ ps.getPkg().getStaticSharedLibName(), ps.getPkg().getStaticSharedLibVersion());
if (libraryInfo == null) {
return false;
}
@@ -2066,7 +2067,7 @@
}
for (String uidPackageName : uidPackageNames) {
- if (ps.name.equals(uidPackageName)) {
+ if (ps.getPackageName().equals(uidPackageName)) {
return false;
}
PackageSetting uidPs = mSettings.getPackageLPr(uidPackageName);
@@ -2076,7 +2077,7 @@
if (index < 0) {
continue;
}
- if (uidPs.pkg.getUsesStaticLibrariesVersions()[index]
+ if (uidPs.getPkg().getUsesStaticLibrariesVersions()[index]
== libraryInfo.getLongVersion()) {
return false;
}
@@ -2211,7 +2212,7 @@
&& (isCallerSameApp(packageName, callingUid)
|| canViewInstantApps(callingUid, userId)
|| mInstantAppRegistry.isInstantAccessGranted(
- userId, UserHandle.getAppId(callingUid), ps.appId));
+ userId, UserHandle.getAppId(callingUid), ps.getAppId()));
if (returnAllowed) {
return ps.getInstantApp(userId);
}
@@ -2368,7 +2369,7 @@
return callerIsInstantApp;
}
// if the target and caller are the same application, don't filter
- if (isCallerSameApp(ps.name, callingUid)) {
+ if (isCallerSameApp(ps.getPackageName(), callingUid)) {
return false;
}
if (callerIsInstantApp) {
@@ -2388,7 +2389,7 @@
return !isComponentVisibleToInstantApp(component, componentType);
}
// request for application; if no components have been explicitly exposed, filter
- return !ps.pkg.isVisibleToInstantApps();
+ return !ps.getPkg().isVisibleToInstantApps();
}
if (ps.getInstantApp(userId)) {
// caller can see all components of all instant applications, don't filter
@@ -2402,7 +2403,7 @@
// request for an instant application; if the caller hasn't been granted access,
//filter
return !mInstantAppRegistry.isInstantAccessGranted(
- userId, UserHandle.getAppId(callingUid), ps.appId);
+ userId, UserHandle.getAppId(callingUid), ps.getAppId());
}
int appId = UserHandle.getAppId(callingUid);
final SettingBase callingPs = mSettings.getSettingLPr(appId);
@@ -2464,7 +2465,7 @@
final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null && ps.isMatch(flags)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
- return UserHandle.getUid(userId, ps.appId);
+ return UserHandle.getUid(userId, ps.getAppId());
}
}
@@ -2735,7 +2736,7 @@
return ((SharedUserSetting) obj).signatures.mSigningDetails;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
- return ps.signatures.mSigningDetails;
+ return ps.getSigningDetails();
}
}
return SigningDetails.UNKNOWN;
@@ -2871,7 +2872,7 @@
case DumpState.DUMP_QUERIES:
{
- final Integer filteringAppId = setting == null ? null : setting.appId;
+ final Integer filteringAppId = setting == null ? null : setting.getAppId();
mAppsFilter.dumpQueries(
pw, filteringAppId, dumpState, mUserManager.getUserIds(),
this::getPackagesForUidInternalBody);
diff --git a/services/core/java/com/android/server/pm/HandlerParams.java b/services/core/java/com/android/server/pm/HandlerParams.java
index 36d41c8..034688b 100644
--- a/services/core/java/com/android/server/pm/HandlerParams.java
+++ b/services/core/java/com/android/server/pm/HandlerParams.java
@@ -41,12 +41,14 @@
@NonNull
final PackageManagerService mPm;
final VerificationHelper mVerificationHelper;
+ final BroadcastHelper mBroadcastHelper;
// TODO(b/198166813): remove PMS dependency
HandlerParams(UserHandle user, PackageManagerService pm) {
mUser = user;
mPm = pm;
mVerificationHelper = new VerificationHelper(mPm.mContext);
+ mBroadcastHelper = new BroadcastHelper(mPm.mInjector);
}
UserHandle getUser() {
@@ -89,7 +91,7 @@
if (dataOwnerPkg == null) {
PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
if (ps != null) {
- dataOwnerPkg = ps.pkg;
+ dataOwnerPkg = ps.getPkg();
}
}
diff --git a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index 4bbdd4c..0ad16c4 100644
--- a/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -185,7 +185,7 @@
/*
* If the package is scanned, it's not erased.
*/
- final AndroidPackage scannedPkg = mPm.mPackages.get(ps.name);
+ final AndroidPackage scannedPkg = mPm.mPackages.get(ps.getPackageName());
if (scannedPkg != null) {
/*
* If the system app is both scanned and in the
@@ -194,23 +194,24 @@
* scanned package so the previously user-installed
* application can be scanned.
*/
- if (mPm.mSettings.isDisabledSystemPackageLPr(ps.name)) {
+ if (mPm.mSettings.isDisabledSystemPackageLPr(ps.getPackageName())) {
logCriticalInfo(Log.WARN,
- "Expecting better updated system app for " + ps.name
+ "Expecting better updated system app for "
+ + ps.getPackageName()
+ "; removing system app. Last known"
+ " codePath=" + ps.getPathString()
- + ", versionCode=" + ps.versionCode
+ + ", versionCode=" + ps.getVersionCode()
+ "; scanned versionCode="
+ scannedPkg.getLongVersionCode());
mPm.removePackageLI(scannedPkg, true);
- mPm.mExpectingBetter.put(ps.name, ps.getPath());
+ mPm.mExpectingBetter.put(ps.getPackageName(), ps.getPath());
}
continue;
}
- if (!mPm.mSettings.isDisabledSystemPackageLPr(ps.name)) {
- logCriticalInfo(Log.WARN, "System package " + ps.name
+ if (!mPm.mSettings.isDisabledSystemPackageLPr(ps.getPackageName())) {
+ logCriticalInfo(Log.WARN, "System package " + ps.getPackageName()
+ " no longer exists; its data will be wiped");
mPm.removePackageDataLIF(ps, userIds, null, 0, false);
} else {
@@ -219,17 +220,17 @@
// still a package. the latter can happen if an OTA keeps the same
// code path, but, changes the package name.
final PackageSetting disabledPs =
- mPm.mSettings.getDisabledSystemPkgLPr(ps.name);
+ mPm.mSettings.getDisabledSystemPkgLPr(ps.getPackageName());
if (disabledPs.getPath() == null || !disabledPs.getPath().exists()
- || disabledPs.pkg == null) {
- possiblyDeletedUpdatedSystemApps.add(ps.name);
+ || disabledPs.getPkg() == null) {
+ possiblyDeletedUpdatedSystemApps.add(ps.getPackageName());
} else {
// We're expecting that the system app should remain disabled, but add
// it to expecting better to recover in case the data version cannot
// be scanned.
// TODO(b/197869066): mExpectingBetter should be able to pulled out into
// this class and used only by the PMS initialization
- mPm.mExpectingBetter.put(disabledPs.name, disabledPs.getPath());
+ mPm.mExpectingBetter.put(disabledPs.getPackageName(), disabledPs.getPath());
}
}
}
@@ -744,7 +745,7 @@
boolean writeSettings, int defParseFlags, List<ScanPartition> dirsToScanAsSystem)
throws SystemDeleteException {
final boolean applyUserRestrictions = outInfo != null && (outInfo.mOrigUsers != null);
- final AndroidPackage deletedPkg = deletedPs.pkg;
+ final AndroidPackage deletedPkg = deletedPs.getPkg();
// Confirm if the system package has been updated
// An updated system app can be deleted. This will also have to restore
// the system pkg from system partition
@@ -771,7 +772,7 @@
outInfo.mIsRemovedPackageSystemUpdate = true;
}
- if (disabledPs.versionCode < deletedPs.versionCode) {
+ if (disabledPs.getVersionCode() < deletedPs.getVersionCode()) {
// Delete data for downgrades
flags &= ~PackageManager.DELETE_KEEP_DATA;
} else {
@@ -789,7 +790,7 @@
// during scan [scanning checks the disabled packages]. We will reverse
// this later, after we've "installed" the stub.
// Reinstate the old system package
- enableSystemPackageLPw(disabledPs.pkg);
+ enableSystemPackageLPw(disabledPs.getPkg());
// Remove any native libraries from the upgraded package.
removeNativeBinariesLI(deletedPs);
}
@@ -806,7 +807,7 @@
// TODO(b/194319951): can we avoid this; throw would come from scan...
throw new SystemDeleteException(e);
} finally {
- if (disabledPs.pkg.isStub()) {
+ if (disabledPs.getPkg().isStub()) {
// We've re-installed the stub; make sure it's disabled here. If package was
// originally enabled, we'll install the compressed version of the application
// and re-enable it afterward.
@@ -829,7 +830,7 @@
private void removeNativeBinariesLI(PackageSetting ps) {
if (ps != null) {
- NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
+ NativeLibraryHelper.removeNativeBinariesLI(ps.getLegacyNativeLibraryPath());
}
}
diff --git a/services/core/java/com/android/server/pm/InstallParams.java b/services/core/java/com/android/server/pm/InstallParams.java
index cb2c99d..c205ecb 100644
--- a/services/core/java/com/android/server/pm/InstallParams.java
+++ b/services/core/java/com/android/server/pm/InstallParams.java
@@ -465,7 +465,7 @@
for (InstallRequest request : apkInstallRequests) {
mPm.restoreAndPostInstall(request.mArgs.mUser.getIdentifier(),
request.mInstallResult,
- new PackageManagerService.PostInstallData(request.mArgs,
+ new PostInstallData(request.mArgs,
request.mInstallResult, null));
}
});
@@ -585,23 +585,25 @@
prepareResult.mPackageToScan, prepareResult.mParseFlags,
prepareResult.mScanFlags, System.currentTimeMillis(),
request.mArgs.mUser, request.mArgs.mAbiOverride);
- if (null != preparedScans.put(result.mPkgSetting.pkg.getPackageName(),
+ if (null != preparedScans.put(result.mPkgSetting.getPkg().getPackageName(),
result)) {
request.mInstallResult.setError(
PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
- "Duplicate package " + result.mPkgSetting.pkg.getPackageName()
+ "Duplicate package "
+ + result.mPkgSetting.getPkg().getPackageName()
+ " in multi-package install request.");
return;
}
createdAppId.put(packageName,
scanPackageHelper.optimisticallyRegisterAppId(result));
- versionInfos.put(result.mPkgSetting.pkg.getPackageName(),
- mPm.getSettingsVersionForPackage(result.mPkgSetting.pkg));
+ versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
+ mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
if (result.mStaticSharedLibraryInfo != null) {
final PackageSetting sharedLibLatestVersionSetting =
mPm.getSharedLibLatestVersionSetting(result);
if (sharedLibLatestVersionSetting != null) {
- lastStaticSharedLibSettings.put(result.mPkgSetting.pkg.getPackageName(),
+ lastStaticSharedLibSettings.put(
+ result.mPkgSetting.getPkg().getPackageName(),
sharedLibLatestVersionSetting);
}
}
@@ -899,8 +901,8 @@
}
}
- if (ps.pkg != null) {
- systemApp = ps.pkg.isSystem();
+ if (ps.getPkg() != null) {
+ systemApp = ps.getPkg().isSystem();
}
res.mOrigUsers = ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true);
}
@@ -1064,8 +1066,8 @@
// We moved the entire application as-is, so bring over the
// previously derived ABI information.
- parsedPackage.setPrimaryCpuAbi(ps.primaryCpuAbiString)
- .setSecondaryCpuAbi(ps.secondaryCpuAbiString);
+ parsedPackage.setPrimaryCpuAbi(ps.getPrimaryCpuAbi())
+ .setSecondaryCpuAbi(ps.getSecondaryCpuAbi());
}
} else {
@@ -1252,7 +1254,7 @@
res.mRemovedInfo = new PackageRemovedInfo(mPm);
res.mRemovedInfo.mUid = oldPackage.getUid();
res.mRemovedInfo.mRemovedPackage = oldPackage.getPackageName();
- res.mRemovedInfo.mInstallerPackageName = ps.installSource.installerPackageName;
+ res.mRemovedInfo.mInstallerPackageName = ps.getInstallSource().installerPackageName;
res.mRemovedInfo.mIsStaticSharedLib =
parsedPackage.getStaticSharedLibName() != null;
res.mRemovedInfo.mIsUpdate = true;
@@ -1392,8 +1394,7 @@
// the scanned package checks out, has signing certificate rotation
// history, and is newer; bring it over
synchronized (mPm.mLock) {
- sourcePackageSetting.signatures.mSigningDetails =
- parsedPackage.getSigningDetails();
+ sourcePackageSetting.setSigningDetails(parsedPackage.getSigningDetails());
}
return true;
} else {
@@ -1544,8 +1545,9 @@
// Set the update and install times
PackageSetting deletedPkgSetting = mPm.getPackageSetting(
oldPackage.getPackageName());
- reconciledPkg.mPkgSetting.firstInstallTime = deletedPkgSetting.firstInstallTime;
- reconciledPkg.mPkgSetting.lastUpdateTime = System.currentTimeMillis();
+ reconciledPkg.mPkgSetting
+ .setFirstInstallTime(deletedPkgSetting.getFirstInstallTime())
+ .setLastUpdateTime(System.currentTimeMillis());
res.mRemovedInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
reconciledPkg.mPkgSetting, request.mAllUsers,
@@ -1590,7 +1592,8 @@
final int[] uidArray = new int[]{oldPackage.getUid()};
final ArrayList<String> pkgList = new ArrayList<>(1);
pkgList.add(oldPackage.getPackageName());
- mPm.sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+ mBroadcastHelper.sendResourcesChangedBroadcast(
+ false, true, pkgList, uidArray, null);
}
// Update the in-memory copy of the previous code paths.
@@ -1615,7 +1618,7 @@
parsedPackage.getPackageName());
if (ps2 != null) {
res.mRemovedInfo.mRemovedForAllUsers =
- mPm.mPackages.get(ps2.name) == null;
+ mPm.mPackages.get(ps2.getPackageName()) == null;
}
}
}
@@ -1728,7 +1731,7 @@
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
}
- mPm.mSettings.addInstallerPackageNames(ps.installSource);
+ mPm.mSettings.addInstallerPackageNames(ps.getInstallSource());
// When replacing an existing package, preserve the original install reason for all
// users that had the package installed before. Similarly for uninstall reasons.
@@ -1771,10 +1774,10 @@
if (IncrementalManager.isIncrementalPath(codePath)
&& mPm.mIncrementalManager != null) {
final IncrementalStatesCallback incrementalStatesCallback =
- new IncrementalStatesCallback(ps.name, mPm);
+ new IncrementalStatesCallback(ps.getPackageName(), mPm);
ps.setIncrementalStatesCallback(incrementalStatesCallback);
mPm.mIncrementalManager.registerLoadingProgressCallback(codePath,
- new IncrementalProgressListener(ps.name, mPm));
+ new IncrementalProgressListener(ps.getPackageName(), mPm));
}
// Ensure that the uninstall reason is UNKNOWN for users with the package installed.
@@ -1837,7 +1840,7 @@
for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) {
final boolean instantApp = ((reconciledPkg.mScanResult.mRequest.mScanFlags
& SCAN_AS_INSTANT_APP) != 0);
- final AndroidPackage pkg = reconciledPkg.mPkgSetting.pkg;
+ final AndroidPackage pkg = reconciledPkg.mPkgSetting.getPkg();
final String packageName = pkg.getPackageName();
final String codePath = pkg.getPath();
final boolean onIncremental = mPm.mIncrementalManager != null
@@ -1978,9 +1981,9 @@
final PackageRemovedInfo pkgRemovedInfo = pkgInstalledInfo.mRemovedInfo;
PackageChangeEvent pkgChangeEvent = new PackageChangeEvent();
- pkgChangeEvent.packageName = pkgSetting.pkg.getPackageName();
- pkgChangeEvent.version = pkgSetting.versionCode;
- pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.lastUpdateTime;
+ pkgChangeEvent.packageName = pkgSetting.getPkg().getPackageName();
+ pkgChangeEvent.version = pkgSetting.getLongVersionCode();
+ pkgChangeEvent.lastUpdateTimeMillis = pkgSetting.getLastUpdateTime();
pkgChangeEvent.newInstalled = (pkgRemovedInfo == null || !pkgRemovedInfo.mIsUpdate);
pkgChangeEvent.dataRemoved = (pkgRemovedInfo != null && pkgRemovedInfo.mDataRemoved);
pkgChangeEvent.isDeleted = false;
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 4709ba1..f0b6ccd 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -388,7 +388,7 @@
// Track instant apps
if (ps.getInstantApp(userId)) {
- addInstantAppLPw(userId, ps.appId);
+ addInstantAppLPw(userId, ps.getAppId());
}
// Remove the in-memory state
@@ -456,12 +456,12 @@
if (ps.getInstantApp(userId)) {
// Add a record for an uninstalled instant app
addUninstalledInstantAppLPw(pkg, userId);
- removeInstantAppLPw(userId, ps.appId);
+ removeInstantAppLPw(userId, ps.getAppId());
} else {
// Deleting an app prunes all instant state such as cookie
deleteDir(getInstantApplicationDir(pkg.getPackageName(), userId));
mCookiePersistence.cancelPendingPersistLPw(pkg, userId);
- removeAppLPw(userId, ps.appId);
+ removeAppLPw(userId, ps.getAppId());
}
}
}
@@ -849,7 +849,7 @@
} else if (lhsPs.getPkgState().getLatestPackageUseTimeInMills() <
rhsPs.getPkgState().getLatestPackageUseTimeInMills()) {
return -1;
- } else if (lhsPs.firstInstallTime > rhsPs.firstInstallTime) {
+ } else if (lhsPs.getFirstInstallTime() > rhsPs.getFirstInstallTime()) {
return 1;
} else {
return -1;
@@ -965,7 +965,7 @@
// TODO(b/135203078): This may be broken due to inner mutability problems that were broken
// as part of moving to PackageInfoUtils. Flags couldn't be determined.
- ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(ps.pkg, 0,
+ ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(ps.getPkg(), 0,
ps.readUserState(userId), userId, ps);
if (addApplicationInfo) {
return new InstantAppInfo(appInfo, requestedPermissions, grantedPermissions);
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 98eef07..8517002 100644
--- a/services/core/java/com/android/server/pm/KeySetManagerService.java
+++ b/services/core/java/com/android/server/pm/KeySetManagerService.java
@@ -138,14 +138,14 @@
if (pkg == null) {
throw new NullPointerException("Invalid package name");
}
- if (pkg.keySetData == null) {
+ if (pkg.getKeySetData() == null) {
throw new NullPointerException("Package has no KeySet data");
}
long id = getIdByKeySetLPr(ks);
if (id == KEYSET_NOT_FOUND) {
return false;
}
- ArraySet<Long> pkgKeys = mKeySetMapping.get(pkg.keySetData.getProperSigningKeySet());
+ ArraySet<Long> pkgKeys = mKeySetMapping.get(pkg.getKeySetData().getProperSigningKeySet());
ArraySet<Long> testKeys = mKeySetMapping.get(id);
return pkgKeys.containsAll(testKeys);
}
@@ -164,8 +164,8 @@
if (pkg == null) {
throw new NullPointerException("Invalid package name");
}
- if (pkg.keySetData == null
- || pkg.keySetData.getProperSigningKeySet()
+ if (pkg.getKeySetData() == null
+ || pkg.getKeySetData().getProperSigningKeySet()
== PackageKeySetData.KEYSET_UNASSIGNED) {
throw new NullPointerException("Package has no KeySet data");
}
@@ -173,7 +173,7 @@
if (id == KEYSET_NOT_FOUND) {
return false;
}
- ArraySet<Long> pkgKeys = mKeySetMapping.get(pkg.keySetData.getProperSigningKeySet());
+ ArraySet<Long> pkgKeys = mKeySetMapping.get(pkg.getKeySetData().getProperSigningKeySet());
ArraySet<Long> testKeys = mKeySetMapping.get(id);
return pkgKeys.equals(testKeys);
}
@@ -242,7 +242,7 @@
ArraySet<PublicKey> signingKeys) {
/* check existing keyset for reuse or removal */
- long signingKeySetId = pkg.keySetData.getProperSigningKeySet();
+ long signingKeySetId = pkg.getKeySetData().getProperSigningKeySet();
if (signingKeySetId != PackageKeySetData.KEYSET_UNASSIGNED) {
ArraySet<PublicKey> existingKeys = getPublicKeysFromKeySetLPr(signingKeySetId);
@@ -260,7 +260,7 @@
/* create and add a new keyset */
KeySetHandle ks = addKeySetLPw(signingKeys);
long id = ks.getId();
- pkg.keySetData.setProperSigningKeySet(id);
+ pkg.getKeySetData().setProperSigningKeySet(id);
return;
}
@@ -284,7 +284,7 @@
*/
void addDefinedKeySetsToPackageLPw(PackageSetting pkg,
Map<String, ArraySet<PublicKey>> definedMapping) {
- ArrayMap<String, Long> prevDefinedKeySets = pkg.keySetData.getAliases();
+ ArrayMap<String, Long> prevDefinedKeySets = pkg.getKeySetData().getAliases();
/* add all of the newly defined KeySets */
Map<String, Long> newKeySetAliases = new ArrayMap<>();
@@ -302,10 +302,10 @@
for (int i = 0; i < prevDefSize; i++) {
decrementKeySetLPw(prevDefinedKeySets.valueAt(i));
}
- pkg.keySetData.removeAllUpgradeKeySets();
+ pkg.getKeySetData().removeAllUpgradeKeySets();
/* switch to the just-added */
- pkg.keySetData.setAliases(newKeySetAliases);
+ pkg.getKeySetData().setAliases(newKeySetAliases);
return;
}
@@ -317,7 +317,7 @@
void addUpgradeKeySetsToPackageLPw(PackageSetting pkg,
Set<String> upgradeAliases) {
for (String upgradeAlias : upgradeAliases) {
- pkg.keySetData.addUpgradeKeySet(upgradeAlias);
+ pkg.getKeySetData().addUpgradeKeySet(upgradeAlias);
}
}
@@ -328,10 +328,10 @@
*/
public KeySetHandle getKeySetByAliasAndPackageNameLPr(String packageName, String alias) {
PackageSetting p = mPackages.get(packageName);
- if (p == null || p.keySetData == null) {
+ if (p == null || p.getKeySetData() == null) {
return null;
}
- Long keySetId = p.keySetData.getAliases().get(alias);
+ Long keySetId = p.getKeySetData().getAliases().get(alias);
if (keySetId == null) {
throw new IllegalArgumentException("Unknown KeySet alias: " + alias);
}
@@ -346,15 +346,15 @@
public boolean shouldCheckUpgradeKeySetLocked(PackageSetting oldPs, int scanFlags) {
// Can't rotate keys during boot or if sharedUser.
if (oldPs == null || (scanFlags&SCAN_INITIAL) != 0 || oldPs.isSharedUser()
- || !oldPs.keySetData.isUsingUpgradeKeySets()) {
+ || !oldPs.getKeySetData().isUsingUpgradeKeySets()) {
return false;
}
// app is using upgradeKeySets; make sure all are valid
- long[] upgradeKeySets = oldPs.keySetData.getUpgradeKeySets();
+ long[] upgradeKeySets = oldPs.getKeySetData().getUpgradeKeySets();
for (int i = 0; i < upgradeKeySets.length; i++) {
if (!isIdValidKeySetId(upgradeKeySets[i])) {
Slog.wtf(TAG, "Package "
- + (oldPs.name != null ? oldPs.name : "<null>")
+ + (oldPs.getPackageName() != null ? oldPs.getPackageName() : "<null>")
+ " contains upgrade-key-set reference to unknown key-set: "
+ upgradeKeySets[i]
+ " reverting to signatures check.");
@@ -367,7 +367,7 @@
public boolean checkUpgradeKeySetLocked(PackageSetting oldPS, AndroidPackage pkg) {
// Upgrade keysets are being used. Determine if new package has a superset of the
// required keys.
- long[] upgradeKeySets = oldPS.keySetData.getUpgradeKeySets();
+ long[] upgradeKeySets = oldPS.getKeySetData().getUpgradeKeySets();
for (int i = 0; i < upgradeKeySets.length; i++) {
Set<PublicKey> upgradeSet = getPublicKeysFromKeySetLPr(upgradeKeySets[i]);
if (upgradeSet != null
@@ -408,12 +408,12 @@
public KeySetHandle getSigningKeySetByPackageNameLPr(String packageName) {
PackageSetting p = mPackages.get(packageName);
if (p == null
- || p.keySetData == null
- || p.keySetData.getProperSigningKeySet()
+ || p.getKeySetData() == null
+ || p.getKeySetData().getProperSigningKeySet()
== PackageKeySetData.KEYSET_UNASSIGNED) {
return null;
}
- return mKeySets.get(p.keySetData.getProperSigningKeySet());
+ return mKeySets.get(p.getKeySetData().getProperSigningKeySet());
}
/**
@@ -578,9 +578,9 @@
PackageSetting pkg = mPackages.get(packageName);
Objects.requireNonNull(pkg, "pkg name: " + packageName
+ "does not have a corresponding entry in mPackages.");
- long signingKeySetId = pkg.keySetData.getProperSigningKeySet();
+ long signingKeySetId = pkg.getKeySetData().getProperSigningKeySet();
decrementKeySetLPw(signingKeySetId);
- ArrayMap<String, Long> definedKeySets = pkg.keySetData.getAliases();
+ ArrayMap<String, Long> definedKeySets = pkg.getKeySetData().getAliases();
for (int i = 0; i < definedKeySets.size(); i++) {
decrementKeySetLPw(definedKeySets.valueAt(i));
}
@@ -591,9 +591,9 @@
}
private void clearPackageKeySetDataLPw(PackageSetting pkg) {
- pkg.keySetData.setProperSigningKeySet(PackageKeySetData.KEYSET_UNASSIGNED);
- pkg.keySetData.removeAllDefinedKeySets();
- pkg.keySetData.removeAllUpgradeKeySets();
+ pkg.getKeySetData().setProperSigningKeySet(PackageKeySetData.KEYSET_UNASSIGNED);
+ pkg.getKeySetData().removeAllDefinedKeySets();
+ pkg.getKeySetData().removeAllUpgradeKeySets();
return;
}
@@ -618,9 +618,10 @@
}
PackageSetting pkg = e.getValue();
pw.print(" ["); pw.print(keySetPackage); pw.println("]");
- if (pkg.keySetData != null) {
+ if (pkg.getKeySetData() != null) {
boolean printedLabel = false;
- for (ArrayMap.Entry<String, Long> entry : pkg.keySetData.getAliases().entrySet()) {
+ for (ArrayMap.Entry<String, Long> entry :
+ pkg.getKeySetData().getAliases().entrySet()) {
if (!printedLabel) {
pw.print(" KeySets Aliases: ");
printedLabel = true;
@@ -635,8 +636,8 @@
pw.println("");
}
printedLabel = false;
- if (pkg.keySetData.isUsingDefinedKeySets()) {
- ArrayMap<String, Long> definedKeySets = pkg.keySetData.getAliases();
+ if (pkg.getKeySetData().isUsingDefinedKeySets()) {
+ ArrayMap<String, Long> definedKeySets = pkg.getKeySetData().getAliases();
final int dksSize = definedKeySets.size();
for (int i = 0; i < dksSize; i++) {
if (!printedLabel) {
@@ -652,12 +653,12 @@
pw.println("");
}
printedLabel = false;
- final long signingKeySet = pkg.keySetData.getProperSigningKeySet();
+ final long signingKeySet = pkg.getKeySetData().getProperSigningKeySet();
pw.print(" Signing KeySets: ");
pw.print(Long.toString(signingKeySet));
pw.println("");
- if (pkg.keySetData.isUsingUpgradeKeySets()) {
- for (long keySetId : pkg.keySetData.getUpgradeKeySets()) {
+ if (pkg.getKeySetData().isUsingUpgradeKeySets()) {
+ for (long keySetId : pkg.getKeySetData().getUpgradeKeySets()) {
if (!printedLabel) {
pw.print(" Upgrade KeySets: ");
printedLabel = true;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 6f02138..edaae47 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -995,11 +995,13 @@
// Note the target activity doesn't have to be exported.
// Flag for bubble
- ActivityOptions options = ActivityOptions.fromBundle(startActivityOptions);
- if (options != null && options.isApplyActivityFlagsForBubbles()) {
- // Flag for bubble to make behaviour match documentLaunchMode=always.
- intents[0].addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
- intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ if (startActivityOptions != null) {
+ ActivityOptions options = ActivityOptions.fromBundle(startActivityOptions);
+ if (options.isApplyActivityFlagsForBubbles()) {
+ // Flag for bubble to make behaviour match documentLaunchMode=always.
+ intents[0].addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+ intents[0].addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+ }
}
intents[0].addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index 4ebf3af4..f8c932d 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -112,7 +112,7 @@
"3rd party apps are not allowed on internal storage");
}
- currentVolumeUuid = ps.volumeUuid;
+ currentVolumeUuid = ps.getVolumeUuid();
final File probe = new File(pkg.getPath());
final File probeOat = new File(probe, "oat");
@@ -137,8 +137,8 @@
isCurrentLocationExternal = pkg.isExternalStorage();
codeFile = new File(pkg.getPath());
- installSource = ps.installSource;
- packageAbiOverride = ps.cpuAbiOverrideString;
+ installSource = ps.getInstallSource();
+ packageAbiOverride = ps.getCpuAbiOverride();
appId = UserHandle.getAppId(pkg.getUid());
seinfo = AndroidPackageUtils.getSeInfo(pkg, ps);
label = String.valueOf(pm.getApplicationLabel(
@@ -350,8 +350,8 @@
final String[] codePaths = { ps.getPathString() };
try {
- mPm.mInstaller.getAppSize(ps.volumeUuid, packageNames, userId, 0,
- ps.appId, ceDataInodes, codePaths, stats);
+ mPm.mInstaller.getAppSize(ps.getVolumeUuid(), packageNames, userId, 0,
+ ps.getAppId(), ceDataInodes, codePaths, stats);
// For now, ignore code size of packages on system partition
if (PackageManagerServiceUtils.isSystemApp(ps)
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 862cb6f..9595e15 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -125,7 +125,7 @@
final List<PackageSetting> important;
final List<PackageSetting> others;
Predicate<PackageSetting> isPlatformPackage = pkgSetting ->
- PLATFORM_PACKAGE_NAME.equals(pkgSetting.pkg.getPackageName());
+ PLATFORM_PACKAGE_NAME.equals(pkgSetting.getPkg().getPackageName());
synchronized (mPackageManagerService.mLock) {
// Important: the packages we need to run with ab-ota compiler-reason.
important = PackageManagerServiceUtils.getPackagesForDexopt(
@@ -144,15 +144,15 @@
}
for (PackageSetting pkgSetting : important) {
- mDexoptCommands.addAll(generatePackageDexopts(pkgSetting.pkg, pkgSetting,
+ mDexoptCommands.addAll(generatePackageDexopts(pkgSetting.getPkg(), pkgSetting,
PackageManagerService.REASON_AB_OTA));
}
for (PackageSetting pkgSetting : others) {
// We assume here that there are no core apps left.
- if (pkgSetting.pkg.isCoreApp()) {
+ if (pkgSetting.getPkg().isCoreApp()) {
throw new IllegalStateException("Found a core app that's not important");
}
- mDexoptCommands.addAll(generatePackageDexopts(pkgSetting.pkg, pkgSetting,
+ mDexoptCommands.addAll(generatePackageDexopts(pkgSetting.getPkg(), pkgSetting,
PackageManagerService.REASON_FIRST_BOOT));
}
completeSize = mDexoptCommands.size();
@@ -162,7 +162,7 @@
Log.i(TAG, "Low on space, deleting oat files in an attempt to free up space: "
+ PackageManagerServiceUtils.packagesToString(others));
for (PackageSetting pkg : others) {
- mPackageManagerService.deleteOatArtifactsOfPackage(pkg.name);
+ mPackageManagerService.deleteOatArtifactsOfPackage(pkg.getPackageName());
}
}
long spaceAvailableNow = getAvailableSpace();
@@ -182,7 +182,7 @@
+ lastUsed.getPkgState().getLatestForegroundPackageUseTimeInMills());
Log.d(TAG, "A/B OTA: deprioritized packages:");
for (PackageSetting pkgSetting : others) {
- Log.d(TAG, " " + pkgSetting.name + " - "
+ Log.d(TAG, " " + pkgSetting.getPackageName() + " - "
+ pkgSetting.getPkgState().getLatestForegroundPackageUseTimeInMills());
}
} catch (Exception ignored) {
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelper.java b/services/core/java/com/android/server/pm/PackageAbiHelper.java
index 24a3e52..ca7c2db 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelper.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelper.java
@@ -134,8 +134,8 @@
//
// If the settings aren't null, sync them up with what we've derived.
if (pkgSetting != null) {
- pkgSetting.primaryCpuAbiString = primary;
- pkgSetting.secondaryCpuAbiString = secondary;
+ pkgSetting.setPrimaryCpuAbi(primary);
+ pkgSetting.setSecondaryCpuAbi(secondary);
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index bf1fe31..1af508f 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -511,15 +511,16 @@
// when scannedPackage is an update of an existing package. Without this check,
// we will never be able to change the ABI of any package belonging to a shared
// user, even if it's compatible with other packages.
- if (scannedPackage != null && scannedPackage.getPackageName().equals(ps.name)) {
+ if (scannedPackage != null && scannedPackage.getPackageName().equals(
+ ps.getPackageName())) {
continue;
}
- if (ps.primaryCpuAbiString == null) {
+ if (ps.getPrimaryCpuAbi() == null) {
continue;
}
final String instructionSet =
- VMRuntime.getInstructionSet(ps.primaryCpuAbiString);
+ VMRuntime.getInstructionSet(ps.getPrimaryCpuAbi());
if (requiredInstructionSet != null && !requiredInstructionSet.equals(instructionSet)) {
// We have a mismatch between instruction sets (say arm vs arm64) warn about
// this but there's not much we can do.
@@ -545,7 +546,7 @@
// scannedPackage did not require an ABI, in which case we have to adjust
// scannedPackage to match the ABI of the set (which is the same as
// requirer's ABI)
- adjustedAbi = requirer.primaryCpuAbiString;
+ adjustedAbi = requirer.getPrimaryCpuAbi();
} else {
// requirer == null implies that we're updating all ABIs in the set to
// match scannedPackage.
diff --git a/services/core/java/com/android/server/pm/PackageFreezer.java b/services/core/java/com/android/server/pm/PackageFreezer.java
index 395f3b4..ecc92b7 100644
--- a/services/core/java/com/android/server/pm/PackageFreezer.java
+++ b/services/core/java/com/android/server/pm/PackageFreezer.java
@@ -62,7 +62,7 @@
ps = mPm.mSettings.getPackageLPr(mPackageName);
}
if (ps != null) {
- mPm.killApplication(ps.name, ps.appId, userId, killReason);
+ mPm.killApplication(ps.getPackageName(), ps.getAppId(), userId, killReason);
}
mCloseGuard.open("close");
}
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index 65acc91..3c3fb56 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -88,11 +88,13 @@
public final class PackageHandler extends Handler {
private final PackageManagerService mPm;
private final VerificationHelper mVerificationHelper;
+ private final BroadcastHelper mBroadcastHelper;
PackageHandler(Looper looper, PackageManagerService pm) {
super(looper);
mPm = pm;
mVerificationHelper = new VerificationHelper(mPm.mContext);
+ mBroadcastHelper = new BroadcastHelper(mPm.mInjector);
}
@Override
@@ -145,7 +147,7 @@
components[i] = componentsToBroadcast.valueAt(index);
final PackageSetting ps = mPm.mSettings.getPackageLPr(packages[i]);
uids[i] = (ps != null)
- ? UserHandle.getUid(packageUserId, ps.appId)
+ ? UserHandle.getUid(packageUserId, ps.getAppId())
: -1;
i++;
}
@@ -164,7 +166,7 @@
case POST_INSTALL: {
if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
- PackageManagerService.PostInstallData data = mPm.mRunningInstalls.get(msg.arg1);
+ PostInstallData data = mPm.mRunningInstalls.get(msg.arg1);
final boolean didRestore = (msg.arg2 != 0);
mPm.mRunningInstalls.delete(msg.arg1);
@@ -640,8 +642,8 @@
packageName /*targetPackage*/,
null /*finishedReceiver*/, updateUserIds, instantUserIds,
null /*broadcastAllowList*/,
- mPm.getTemporaryAppAllowlistBroadcastOptions(REASON_PACKAGE_REPLACED)
- .toBundle());
+ mBroadcastHelper.getTemporaryAppAllowlistBroadcastOptions(
+ REASON_PACKAGE_REPLACED).toBundle());
} else if (launchedForRestore && !res.mPkg.isSystem()) {
// First-install and we did a restore, so we're responsible for the
// first-launch broadcast.
@@ -649,7 +651,7 @@
Slog.i(TAG, "Post-restore of " + packageName
+ " sending FIRST_LAUNCH in " + Arrays.toString(firstUserIds));
}
- mPm.sendFirstLaunchBroadcast(packageName, installerPackage,
+ mBroadcastHelper.sendFirstLaunchBroadcast(packageName, installerPackage,
firstUserIds, firstInstantUserIds);
}
@@ -678,7 +680,8 @@
final int[] uidArray = new int[]{res.mPkg.getUid()};
ArrayList<String> pkgList = new ArrayList<>(1);
pkgList.add(packageName);
- mPm.sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
+ mBroadcastHelper.sendResourcesChangedBroadcast(
+ true, true, pkgList, uidArray, null);
}
} else if (!ArrayUtils.isEmpty(res.mLibraryConsumers)) { // if static shared lib
for (int i = 0; i < res.mLibraryConsumers.size(); i++) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index b34a3a2..e8ba176 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -59,6 +59,7 @@
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SELinux;
+import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.stats.devicepolicy.DevicePolicyEnums;
@@ -135,6 +136,8 @@
private static final long MAX_ACTIVE_SESSIONS_NO_PERMISSION = 50;
/** Upper bound on number of historical sessions for a UID */
private static final long MAX_HISTORICAL_SESSIONS = 1048576;
+ /** Destroy sessions older than this on storage free request */
+ private static final long MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS = 8 * DateUtils.HOUR_IN_MILLIS;
/**
* Allow verification-skipping if it's a development app installed through ADB with
@@ -339,22 +342,28 @@
@GuardedBy("mSessions")
private void reconcileStagesLocked(String volumeUuid) {
- final File stagingDir = getTmpSessionDir(volumeUuid);
- final ArraySet<File> unclaimedStages = newArraySet(
- stagingDir.listFiles(sStageFilter));
-
- // We also need to clean up orphaned staging directory for staged sessions
- final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid);
- unclaimedStages.addAll(newArraySet(stagedSessionStagingDir.listFiles()));
-
+ final ArraySet<File> unclaimedStages = getStagingDirsOnVolume(volumeUuid);
// Ignore stages claimed by active sessions
for (int i = 0; i < mSessions.size(); i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
unclaimedStages.remove(session.stageDir);
}
+ removeStagingDirs(unclaimedStages);
+ }
+ private ArraySet<File> getStagingDirsOnVolume(String volumeUuid) {
+ final File stagingDir = getTmpSessionDir(volumeUuid);
+ final ArraySet<File> stagingDirs = newArraySet(stagingDir.listFiles(sStageFilter));
+
+ // We also need to clean up orphaned staging directory for staged sessions
+ final File stagedSessionStagingDir = Environment.getDataStagingDirectory(volumeUuid);
+ stagingDirs.addAll(newArraySet(stagedSessionStagingDir.listFiles()));
+ return stagingDirs;
+ }
+
+ private void removeStagingDirs(ArraySet<File> stagingDirsToRemove) {
// Clean up orphaned staging directories
- for (File stage : unclaimedStages) {
+ for (File stage : stagingDirsToRemove) {
Slog.w(TAG, "Deleting orphan stage " + stage);
synchronized (mPm.mInstallLock) {
mPm.removeCodePathLI(stage);
@@ -368,6 +377,33 @@
}
}
+ /**
+ * Called to free up some storage space from obsolete installation files
+ */
+ public void freeStageDirs(String volumeUuid) {
+ final ArraySet<File> unclaimedStagingDirsOnVolume = getStagingDirsOnVolume(volumeUuid);
+ final long currentTimeMillis = System.currentTimeMillis();
+ synchronized (mSessions) {
+ for (int i = 0; i < mSessions.size(); i++) {
+ final PackageInstallerSession session = mSessions.valueAt(i);
+ if (!unclaimedStagingDirsOnVolume.contains(session.stageDir)) {
+ // Only handles sessions stored on the target volume
+ continue;
+ }
+ final long age = currentTimeMillis - session.createdMillis;
+ if (age >= MAX_SESSION_AGE_ON_LOW_STORAGE_MILLIS) {
+ // Aggressively close old sessions because we are running low on storage
+ // Their staging dirs will be removed too
+ session.abandon();
+ } else {
+ // Session is new enough, so it deserves to be kept even on low storage
+ unclaimedStagingDirsOnVolume.remove(session.stageDir);
+ }
+ }
+ }
+ removeStagingDirs(unclaimedStagingDirsOnVolume);
+ }
+
public static boolean isStageName(String name) {
final boolean isFile = name.startsWith("vmdl") && name.endsWith(".tmp");
final boolean isContainer = name.startsWith("smdl") && name.endsWith(".tmp");
@@ -1462,7 +1498,7 @@
if (mOkToSendBroadcasts && !session.isDestroyed()) {
// we don't scrub the data here as this is sent only to the installer several
// privileged system packages
- mPm.sendSessionUpdatedBroadcast(
+ sendSessionUpdatedBroadcast(
session.generateInfoForCaller(false/*icon*/, Process.SYSTEM_UID),
session.userId);
}
@@ -1507,4 +1543,19 @@
mSettingsWriteRequest.runNow();
}
}
+
+ /**
+ * Send a {@code PackageInstaller.ACTION_SESSION_UPDATED} broadcast intent, containing
+ * the {@code sessionInfo} in the extra field {@code PackageInstaller.EXTRA_SESSION}.
+ */
+ private void sendSessionUpdatedBroadcast(PackageInstaller.SessionInfo sessionInfo,
+ int userId) {
+ if (TextUtils.isEmpty(sessionInfo.installerPackageName)) {
+ return;
+ }
+ Intent sessionUpdatedIntent = new Intent(PackageInstaller.ACTION_SESSION_UPDATED)
+ .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
+ .setPackage(sessionInfo.installerPackageName);
+ mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId));
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 0f5f293..19645cd 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -687,7 +687,7 @@
final Runnable r;
synchronized (mLock) {
assertNotChild("StagedSession#abandon");
- assertCallerIsOwnerOrRoot();
+ assertCallerIsOwnerOrRootOrSystem();
if (isInTerminalState()) {
// We keep the session in the database if it's in a finalized state. It will be
// removed by PackageInstallerService when the last update time is old enough.
@@ -1937,11 +1937,11 @@
private static boolean isIncrementalInstallationAllowed(String packageName) {
final PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
final PackageSetting existingPkgSetting = pmi.getPackageSetting(packageName);
- if (existingPkgSetting == null || existingPkgSetting.pkg == null) {
+ if (existingPkgSetting == null || existingPkgSetting.getPkg() == null) {
return true;
}
- return !existingPkgSetting.pkg.isSystem()
+ return !existingPkgSetting.getPkg().isSystem()
&& !existingPkgSetting.getPkgState().isUpdatedSystemApp();
}
@@ -3784,7 +3784,7 @@
private void abandonNonStaged() {
synchronized (mLock) {
assertNotChild("abandonNonStaged");
- assertCallerIsOwnerOrRoot();
+ assertCallerIsOwnerOrRootOrSystem();
if (mRelinquished) {
if (LOGD) Slog.d(TAG, "Ignoring abandon after commit relinquished control");
return;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e76668f..a10f4c3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -48,8 +48,6 @@
import static android.content.pm.PackageManager.TYPE_UNKNOWN;
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
-import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
-import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
@@ -78,10 +76,8 @@
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.ActivityManager;
-import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.ApplicationPackageManager;
-import android.app.BroadcastOptions;
import android.app.IActivityManager;
import android.app.PendingIntent;
import android.app.admin.IDevicePolicyManager;
@@ -184,14 +180,12 @@
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelableException;
import android.os.PersistableBundle;
-import android.os.PowerWhitelistManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -410,7 +404,6 @@
static final boolean DEBUG_BACKUP = false;
public static final boolean DEBUG_INSTALL = false;
public static final boolean DEBUG_REMOVE = false;
- private static final boolean DEBUG_BROADCASTS = false;
static final boolean DEBUG_PACKAGE_INFO = false;
static final boolean DEBUG_INTENT_MATCHING = false;
public static final boolean DEBUG_PACKAGE_SCANNING = false;
@@ -563,12 +556,6 @@
private static final long DEFAULT_MANDATORY_FSTRIM_INTERVAL = 3 * DateUtils.DAY_IN_MILLIS;
/**
- * The default maximum time to wait for the verification agent to return in
- * milliseconds.
- */
- static final long DEFAULT_VERIFICATION_TIMEOUT = 10 * 1000;
-
- /**
* Default IncFs timeouts. Maximum values in IncFs is 1hr.
*
* <p>If flag value is empty, the default value will be assigned.
@@ -628,7 +615,7 @@
static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
- private static final String PACKAGE_SCHEME = "package";
+ static final String PACKAGE_SCHEME = "package";
private static final String COMPANION_PACKAGE_NAME = "com.android.companiondevicemanager";
@@ -663,12 +650,6 @@
*/
private static final boolean FORCE_PACKAGE_PARSED_CACHE_ENABLED = false;
- /**
- * Permissions required in order to receive instant application lifecycle broadcasts.
- */
- private static final String[] INSTANT_APP_BROADCAST_PERMISSION =
- new String[] { android.Manifest.permission.ACCESS_INSTANT_APPS };
-
static final String RANDOM_DIR_PREFIX = "~~";
final Handler mHandler;
@@ -826,7 +807,7 @@
final ApexManager mApexManager;
- final Injector mInjector;
+ final PackageManagerServiceInjector mInjector;
/**
* The list of all system partitions that may contain packages in ascending order of
@@ -849,318 +830,8 @@
private static final PerUidReadTimeouts[] EMPTY_PER_UID_READ_TIMEOUTS_ARRAY = {};
- /**
- * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
- *
- * NOTE: All getters should return the same instance for every call.
- */
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- public static class Injector {
-
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- interface Producer<T> {
- /** Produce an instance of type {@link T} */
- T produce(Injector injector, PackageManagerService packageManager);
- }
-
- interface ProducerWithArgument<T, R> {
- T produce(Injector injector, PackageManagerService packageManager, R argument);
- }
-
- interface ServiceProducer {
- <T> T produce(Class<T> c);
- }
-
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- static class Singleton<T> {
- private final Producer<T> mProducer;
- private volatile T mInstance = null;
- Singleton(Producer<T> producer) {
- this.mProducer = producer;
- }
- T get(Injector injector, PackageManagerService packageManagerService) {
- if (mInstance == null) {
- mInstance = mProducer.produce(injector, packageManagerService);
- }
- return mInstance;
- }
- }
-
- private PackageManagerService mPackageManager;
-
- private final PackageAbiHelper mAbiHelper;
- private final Context mContext;
- private final PackageManagerTracedLock mLock;
- private final Installer mInstaller;
- private final Object mInstallLock;
- private final Handler mBackgroundHandler;
- private final Executor mBackgroundExecutor;
- private final List<ScanPartition> mSystemPartitions;
-
- // ----- producers -----
- private final Singleton<ComponentResolver> mComponentResolverProducer;
- private final Singleton<PermissionManagerServiceInternal> mPermissionManagerServiceProducer;
- private final Singleton<UserManagerService> mUserManagerProducer;
- private final Singleton<Settings> mSettingsProducer;
- private final Singleton<AppsFilter> mAppsFilterProducer;
- private final Singleton<PlatformCompat> mPlatformCompatProducer;
- private final Singleton<SystemConfig> mSystemConfigProducer;
- private final Singleton<PackageDexOptimizer> mPackageDexOptimizerProducer;
- private final Singleton<DexManager> mDexManagerProducer;
- private final Singleton<ArtManagerService> mArtManagerServiceProducer;
- private final Singleton<ApexManager> mApexManagerProducer;
- private final Singleton<ViewCompiler> mViewCompilerProducer;
- private final Singleton<IncrementalManager> mIncrementalManagerProducer;
- private final Singleton<DefaultAppProvider> mDefaultAppProviderProducer;
- private final Singleton<DisplayMetrics> mDisplayMetricsProducer;
- private final Producer<PackageParser2> mScanningCachingPackageParserProducer;
- private final Producer<PackageParser2> mScanningPackageParserProducer;
- private final Producer<PackageParser2> mPreparingPackageParserProducer;
- private final Singleton<PackageInstallerService> mPackageInstallerServiceProducer;
- private final ProducerWithArgument<InstantAppResolverConnection, ComponentName>
- mInstantAppResolverConnectionProducer;
- private final Singleton<LegacyPermissionManagerInternal>
- mLegacyPermissionManagerInternalProducer;
- private final SystemWrapper mSystemWrapper;
- private final ServiceProducer mGetLocalServiceProducer;
- private final ServiceProducer mGetSystemServiceProducer;
- private final Singleton<ModuleInfoProvider> mModuleInfoProviderProducer;
- private final Singleton<DomainVerificationManagerInternal>
- mDomainVerificationManagerInternalProducer;
- private final Singleton<Handler> mHandlerProducer;
-
- Injector(Context context, PackageManagerTracedLock lock, Installer installer,
- Object installLock, PackageAbiHelper abiHelper,
- Handler backgroundHandler,
- List<ScanPartition> systemPartitions,
- Producer<ComponentResolver> componentResolverProducer,
- Producer<PermissionManagerServiceInternal> permissionManagerServiceProducer,
- Producer<UserManagerService> userManagerProducer,
- Producer<Settings> settingsProducer,
- Producer<AppsFilter> appsFilterProducer,
- Producer<PlatformCompat> platformCompatProducer,
- Producer<SystemConfig> systemConfigProducer,
- Producer<PackageDexOptimizer> packageDexOptimizerProducer,
- Producer<DexManager> dexManagerProducer,
- Producer<ArtManagerService> artManagerServiceProducer,
- Producer<ApexManager> apexManagerProducer,
- Producer<ViewCompiler> viewCompilerProducer,
- Producer<IncrementalManager> incrementalManagerProducer,
- Producer<DefaultAppProvider> defaultAppProviderProducer,
- Producer<DisplayMetrics> displayMetricsProducer,
- Producer<PackageParser2> scanningCachingPackageParserProducer,
- Producer<PackageParser2> scanningPackageParserProducer,
- Producer<PackageParser2> preparingPackageParserProducer,
- Producer<PackageInstallerService> packageInstallerServiceProducer,
- ProducerWithArgument<InstantAppResolverConnection, ComponentName>
- instantAppResolverConnectionProducer,
- Producer<ModuleInfoProvider> moduleInfoProviderProducer,
- Producer<LegacyPermissionManagerInternal> legacyPermissionManagerInternalProducer,
- Producer<DomainVerificationManagerInternal>
- domainVerificationManagerInternalProducer,
- Producer<Handler> handlerProducer,
- SystemWrapper systemWrapper,
- ServiceProducer getLocalServiceProducer,
- ServiceProducer getSystemServiceProducer) {
- mContext = context;
- mLock = lock;
- mInstaller = installer;
- mAbiHelper = abiHelper;
- mInstallLock = installLock;
- mBackgroundHandler = backgroundHandler;
- mBackgroundExecutor = new HandlerExecutor(backgroundHandler);
- mSystemPartitions = systemPartitions;
- mComponentResolverProducer = new Singleton<>(componentResolverProducer);
- mPermissionManagerServiceProducer = new Singleton<>(permissionManagerServiceProducer);
- mUserManagerProducer = new Singleton<>(userManagerProducer);
- mSettingsProducer = new Singleton<>(settingsProducer);
- mAppsFilterProducer = new Singleton<>(appsFilterProducer);
- mPlatformCompatProducer = new Singleton<>(platformCompatProducer);
- mSystemConfigProducer = new Singleton<>(systemConfigProducer);
- mPackageDexOptimizerProducer = new Singleton<>(packageDexOptimizerProducer);
- mDexManagerProducer = new Singleton<>(dexManagerProducer);
- mArtManagerServiceProducer = new Singleton<>(artManagerServiceProducer);
- mApexManagerProducer = new Singleton<>(apexManagerProducer);
- mViewCompilerProducer = new Singleton<>(viewCompilerProducer);
- mIncrementalManagerProducer = new Singleton<>(incrementalManagerProducer);
- mDefaultAppProviderProducer = new Singleton<>(defaultAppProviderProducer);
- mDisplayMetricsProducer = new Singleton<>(displayMetricsProducer);
- mScanningCachingPackageParserProducer = scanningCachingPackageParserProducer;
- mScanningPackageParserProducer = scanningPackageParserProducer;
- mPreparingPackageParserProducer = preparingPackageParserProducer;
- mPackageInstallerServiceProducer = new Singleton<>(packageInstallerServiceProducer);
- mInstantAppResolverConnectionProducer = instantAppResolverConnectionProducer;
- mModuleInfoProviderProducer = new Singleton<>(moduleInfoProviderProducer);
- mLegacyPermissionManagerInternalProducer = new Singleton<>(
- legacyPermissionManagerInternalProducer);
- mSystemWrapper = systemWrapper;
- mGetLocalServiceProducer = getLocalServiceProducer;
- mGetSystemServiceProducer = getSystemServiceProducer;
- mDomainVerificationManagerInternalProducer =
- new Singleton<>(domainVerificationManagerInternalProducer);
- mHandlerProducer = new Singleton<>(handlerProducer);
- }
-
- /**
- * Bootstraps this injector with the {@link PackageManagerService instance to which it
- * belongs.
- */
- public void bootstrap(PackageManagerService pm) {
- this.mPackageManager = pm;
- }
-
- public UserManagerInternal getUserManagerInternal() {
- return getUserManagerService().getInternalForInjectorOnly();
- }
-
- public PackageAbiHelper getAbiHelper() {
- return mAbiHelper;
- }
-
- public Object getInstallLock() {
- return mInstallLock;
- }
-
- public List<ScanPartition> getSystemPartitions() {
- return mSystemPartitions;
- }
-
- public UserManagerService getUserManagerService() {
- return mUserManagerProducer.get(this, mPackageManager);
- }
-
- public PackageManagerTracedLock getLock() {
- return mLock;
- }
-
- public Installer getInstaller() {
- return mInstaller;
- }
-
- public ComponentResolver getComponentResolver() {
- return mComponentResolverProducer.get(this, mPackageManager);
- }
-
- public PermissionManagerServiceInternal getPermissionManagerServiceInternal() {
- return mPermissionManagerServiceProducer.get(this, mPackageManager);
- }
-
- public Context getContext() {
- return mContext;
- }
-
- public Settings getSettings() {
- return mSettingsProducer.get(this, mPackageManager);
- }
-
- public AppsFilter getAppsFilter() {
- return mAppsFilterProducer.get(this, mPackageManager);
- }
-
- public PlatformCompat getCompatibility() {
- return mPlatformCompatProducer.get(this, mPackageManager);
- }
-
- public SystemConfig getSystemConfig() {
- return mSystemConfigProducer.get(this, mPackageManager);
- }
-
- public PackageDexOptimizer getPackageDexOptimizer() {
- return mPackageDexOptimizerProducer.get(this, mPackageManager);
- }
-
- public DexManager getDexManager() {
- return mDexManagerProducer.get(this, mPackageManager);
- }
-
- public ArtManagerService getArtManagerService() {
- return mArtManagerServiceProducer.get(this, mPackageManager);
- }
-
- public ApexManager getApexManager() {
- return mApexManagerProducer.get(this, mPackageManager);
- }
-
- public ViewCompiler getViewCompiler() {
- return mViewCompilerProducer.get(this, mPackageManager);
- }
-
- public Handler getBackgroundHandler() {
- return mBackgroundHandler;
- }
-
- public Executor getBackgroundExecutor() {
- return mBackgroundExecutor;
- }
-
- public DisplayMetrics getDisplayMetrics() {
- return mDisplayMetricsProducer.get(this, mPackageManager);
- }
-
- public <T> T getLocalService(Class<T> c) {
- return mGetLocalServiceProducer.produce(c);
- }
-
- public <T> T getSystemService(Class<T> c) {
- return mGetSystemServiceProducer.produce(c);
- }
-
- public SystemWrapper getSystemWrapper() {
- return mSystemWrapper;
- }
-
- public IncrementalManager getIncrementalManager() {
- return mIncrementalManagerProducer.get(this, mPackageManager);
- }
-
- public DefaultAppProvider getDefaultAppProvider() {
- return mDefaultAppProviderProducer.get(this, mPackageManager);
- }
-
- public PackageParser2 getScanningCachingPackageParser() {
- return mScanningCachingPackageParserProducer.produce(this, mPackageManager);
- }
- public PackageParser2 getScanningPackageParser() {
- return mScanningPackageParserProducer.produce(this, mPackageManager);
- }
- public PackageParser2 getPreparingPackageParser() {
- return mPreparingPackageParserProducer.produce(this, mPackageManager);
- }
-
- public PackageInstallerService getPackageInstallerService() {
- return mPackageInstallerServiceProducer.get(this, mPackageManager);
- }
-
- public InstantAppResolverConnection getInstantAppResolverConnection(
- ComponentName instantAppResolverComponent) {
- return mInstantAppResolverConnectionProducer.produce(
- this, mPackageManager, instantAppResolverComponent);
- }
-
- public ModuleInfoProvider getModuleInfoProvider() {
- return mModuleInfoProviderProducer.get(this, mPackageManager);
- }
-
- public LegacyPermissionManagerInternal getLegacyPermissionManagerInternal() {
- return mLegacyPermissionManagerInternalProducer.get(this, mPackageManager);
- }
-
- public DomainVerificationManagerInternal getDomainVerificationManagerInternal() {
- return mDomainVerificationManagerInternalProducer.get(this, mPackageManager);
- }
-
- public Handler getHandler() {
- return mHandlerProducer.get(this, mPackageManager);
- }
- }
-
- /** Provides an abstraction to static access to system state. */
- public interface SystemWrapper {
- void disablePackageCaches();
- void enablePackageCaches();
- }
-
- private static class DefaultSystemWrapper implements SystemWrapper {
+ private static class DefaultSystemWrapper implements
+ PackageManagerServiceInjector.SystemWrapper {
@Override
public void disablePackageCaches() {
@@ -1189,69 +860,6 @@
}
}
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- public static class TestParams {
- public ApexManager apexManager;
- public @Nullable String appPredictionServicePackage;
- public ArtManagerService artManagerService;
- public @Nullable String configuratorPackage;
- public int defParseFlags;
- public DefaultAppProvider defaultAppProvider;
- public DexManager dexManager;
- public List<ScanPartition> dirsToScanAsSystem;
- public boolean factoryTest;
- public ArrayMap<String, FeatureInfo> availableFeatures;
- public Handler handler;
- public @Nullable String incidentReportApproverPackage;
- public IncrementalManager incrementalManager;
- public PackageInstallerService installerService;
- public InstantAppRegistry instantAppRegistry;
- public InstantAppResolverConnection instantAppResolverConnection;
- public ComponentName instantAppResolverSettingsComponent;
- public boolean isPreNmr1Upgrade;
- public boolean isPreNupgrade;
- public boolean isPreQupgrade;
- public boolean isUpgrade;
- public LegacyPermissionManagerInternal legacyPermissionManagerInternal;
- public DisplayMetrics Metrics;
- public ModuleInfoProvider moduleInfoProvider;
- public MovePackageHelper.MoveCallbacks moveCallbacks;
- public boolean onlyCore;
- public OverlayConfig overlayConfig;
- public PackageDexOptimizer packageDexOptimizer;
- public PackageParser2.Callback packageParserCallback;
- public PendingPackageBroadcasts pendingPackageBroadcasts;
- public PackageManagerInternal pmInternal;
- public TestUtilityService testUtilityService;
- public ProcessLoggingHandler processLoggingHandler;
- public ProtectedPackages protectedPackages;
- public @NonNull String requiredInstallerPackage;
- public @NonNull String requiredPermissionControllerPackage;
- public @NonNull String requiredUninstallerPackage;
- public @Nullable String requiredVerifierPackage;
- public String[] separateProcesses;
- public @NonNull String servicesExtensionPackageName;
- public @Nullable String setupWizardPackage;
- public @NonNull String sharedSystemSharedLibraryPackageName;
- public @Nullable String storageManagerPackage;
- public @Nullable String defaultTextClassifierPackage;
- public @Nullable String systemTextClassifierPackage;
- public @Nullable String overlayConfigSignaturePackage;
- public ViewCompiler viewCompiler;
- public @Nullable String retailDemoPackage;
- public @Nullable String recentsPackage;
- public ComponentName resolveComponentName;
- public ArrayMap<String, AndroidPackage> packages;
- public boolean enableFreeCacheV2;
- public int sdkVersion;
- public File appInstallDir;
- public File appLib32InstallDir;
- public boolean isEngBuild;
- public boolean isUserDebugBuild;
- public int sdkInt = Build.VERSION.SDK_INT;
- public final String incrementalVersion = Build.VERSION.INCREMENTAL;
- }
-
@Watched
final AppsFilter mAppsFilter;
@@ -1323,7 +931,6 @@
volatile boolean mSystemReady;
@Watched(manual = true)
private volatile boolean mSafeMode;
- volatile boolean mHasSystemUidErrors;
@Watched
private final WatchedSparseBooleanArray mWebInstantAppsDisabled =
new WatchedSparseBooleanArray();
@@ -1381,71 +988,6 @@
private final PackageProperty mPackageProperty = new PackageProperty();
- // Set of pending broadcasts for aggregating enable/disable of components.
- @VisibleForTesting(visibility = Visibility.PACKAGE)
- public static class PendingPackageBroadcasts {
- // for each user id, a map of <package name -> components within that package>
- final SparseArray<ArrayMap<String, ArrayList<String>>> mUidMap;
-
- public PendingPackageBroadcasts() {
- mUidMap = new SparseArray<>(2);
- }
-
- public ArrayList<String> get(int userId, String packageName) {
- ArrayMap<String, ArrayList<String>> packages = getOrAllocate(userId);
- return packages.get(packageName);
- }
-
- public void put(int userId, String packageName, ArrayList<String> components) {
- ArrayMap<String, ArrayList<String>> packages = getOrAllocate(userId);
- packages.put(packageName, components);
- }
-
- public void remove(int userId, String packageName) {
- ArrayMap<String, ArrayList<String>> packages = mUidMap.get(userId);
- if (packages != null) {
- packages.remove(packageName);
- }
- }
-
- public void remove(int userId) {
- mUidMap.remove(userId);
- }
-
- public int userIdCount() {
- return mUidMap.size();
- }
-
- public int userIdAt(int n) {
- return mUidMap.keyAt(n);
- }
-
- public ArrayMap<String, ArrayList<String>> packagesForUserId(int userId) {
- return mUidMap.get(userId);
- }
-
- public int size() {
- // total number of pending broadcast entries across all userIds
- int num = 0;
- for (int i = 0; i< mUidMap.size(); i++) {
- num += mUidMap.valueAt(i).size();
- }
- return num;
- }
-
- public void clear() {
- mUidMap.clear();
- }
-
- private ArrayMap<String, ArrayList<String>> getOrAllocate(int userId) {
- ArrayMap<String, ArrayList<String>> map = mUidMap.get(userId);
- if (map == null) {
- map = new ArrayMap<>();
- mUidMap.put(userId, map);
- }
- return map;
- }
- }
final PendingPackageBroadcasts mPendingBroadcasts;
static final int SEND_PENDING_BROADCAST = 1;
@@ -1487,24 +1029,6 @@
// Stores a list of users whose package restrictions file needs to be updated
final ArraySet<Integer> mDirtyUsers = new ArraySet<>();
- // Recordkeeping of restore-after-install operations that are currently in flight
- // between the Package Manager and the Backup Manager
- static class PostInstallData {
- @Nullable
- public final InstallArgs args;
- @NonNull
- public final PackageInstalledInfo res;
- @Nullable
- public final Runnable mPostInstallRunnable;
-
- PostInstallData(@Nullable InstallArgs args, @NonNull PackageInstalledInfo res,
- @Nullable Runnable postInstallRunnable) {
- this.args = args;
- this.res = res;
- mPostInstallRunnable = postInstallRunnable;
- }
- }
-
final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<>();
int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows
@@ -1535,6 +1059,8 @@
private final DomainVerificationConnection mDomainVerificationConnection;
+ private final BroadcastHelper mBroadcastHelper;
+
/**
* Invalidate the package info cache, which includes updating the cached computer.
* @hide
@@ -1914,7 +1440,7 @@
}
}
- void scheduleWritePackageListLocked(int userId) {
+ private void scheduleWritePackageListLocked(int userId) {
invalidatePackageInfoCache();
if (!mHandler.hasMessages(WRITE_PACKAGE_LIST)) {
Message msg = mHandler.obtainMessage(WRITE_PACKAGE_LIST);
@@ -1923,12 +1449,12 @@
}
}
- void scheduleWritePackageRestrictionsLocked(UserHandle user) {
+ private void scheduleWritePackageRestrictionsLocked(UserHandle user) {
final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
scheduleWritePackageRestrictionsLocked(userId);
}
- void scheduleWritePackageRestrictionsLocked(int userId) {
+ private void scheduleWritePackageRestrictionsLocked(int userId) {
invalidatePackageInfoCache();
final int[] userIds = (userId == UserHandle.USER_ALL)
? mUserManager.getUserIds() : new int[]{userId};
@@ -1956,7 +1482,7 @@
backgroundThread.start();
Handler backgroundHandler = new Handler(backgroundThread.getLooper());
- Injector injector = new Injector(
+ PackageManagerServiceInjector injector = new PackageManagerServiceInjector(
context, lock, installer, installLock, new PackageAbiHelperImpl(),
backgroundHandler,
SYSTEM_PARTITIONS,
@@ -2034,7 +1560,7 @@
Slog.e(TAG, "Failed to find package setting " + packageName);
return;
}
- pkg = ps.pkg;
+ pkg = ps.getPkg();
sharedUser = ps.getSharedUser();
oldSeInfo = AndroidPackageUtils.getSeInfo(pkg, ps);
}
@@ -2137,7 +1663,8 @@
* none of the initialization is needed.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
- public PackageManagerService(@NonNull Injector injector, @NonNull TestParams testParams) {
+ public PackageManagerService(@NonNull PackageManagerServiceInjector injector,
+ @NonNull PackageManagerServiceTestParams testParams) {
mInjector = injector;
mInjector.bootstrap(this);
mAppsFilter = injector.getAppsFilter();
@@ -2220,11 +1747,13 @@
mIncrementalVersion = testParams.incrementalVersion;
mDomainVerificationConnection = new DomainVerificationConnection(this);
+ mBroadcastHelper = new BroadcastHelper(mInjector);
+
invalidatePackageInfoCache();
}
- public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest,
- final String buildFingerprint, final boolean isEngBuild,
+ public PackageManagerService(PackageManagerServiceInjector injector, boolean onlyCore,
+ boolean factoryTest, final String buildFingerprint, final boolean isEngBuild,
final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
mIsEngBuild = isEngBuild;
mIsUserDebugBuild = isUserDebugBuild;
@@ -2363,6 +1892,8 @@
mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
mDomainVerificationManager.setConnection(mDomainVerificationConnection);
+ mBroadcastHelper = new BroadcastHelper(mInjector);
+
synchronized (mLock) {
// Create the computer as soon as the state objects have been installed. The
// cached computer is the same as the live computer until the end of the
@@ -2479,7 +2010,7 @@
if (isDeviceUpgrading()) {
mExistingPackages = new ArraySet<>(packageSettings.size());
for (PackageSetting ps : packageSettings.values()) {
- mExistingPackages.add(ps.name);
+ mExistingPackages.add(ps.getPackageName());
}
}
@@ -2634,7 +2165,7 @@
synchronized (mLock) {
PackageSetting ps = mSettings.getPackageLPr(pkgName);
if (ps != null && ps.getInstalled(UserHandle.USER_SYSTEM)) {
- pkg = ps.pkg;
+ pkg = ps.getPkg();
}
}
if (pkg != null) {
@@ -2659,9 +2190,9 @@
Slog.i(TAG, "Build fingerprint changed; clearing code caches");
for (int i = 0; i < packageSettings.size(); i++) {
final PackageSetting ps = packageSettings.valueAt(i);
- if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
+ if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.getVolumeUuid())) {
// No apps are running this early, so no need to freeze
- clearAppDataLIF(ps.pkg, UserHandle.USER_ALL,
+ clearAppDataLIF(ps.getPkg(), UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
| Installer.FLAG_CLEAR_CODE_CACHE_ONLY
| Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
@@ -2744,7 +2275,7 @@
if (ps == null || !ps.getInstantApp(userId) || !ps.getInstalled(userId)) {
continue;
}
- mInstantAppRegistry.addInstantAppLPw(userId, ps.appId);
+ mInstantAppRegistry.addInstantAppLPw(userId, ps.getAppId());
}
}
@@ -3262,7 +2793,6 @@
throw new SecurityException("Package " + packageName + " is not encryption aware!");
case PACKAGE_STARTABILITY_OK:
default:
- return;
}
}
@@ -3284,7 +2814,7 @@
return PACKAGE_STARTABILITY_FROZEN;
}
- if (!userKeyUnlocked && !AndroidPackageUtils.isEncryptionAware(ps.pkg)) {
+ if (!userKeyUnlocked && !AndroidPackageUtils.isEncryptionAware(ps.getPkg())) {
return PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED;
}
}
@@ -3394,14 +2924,14 @@
for (int i=names.length-1; i>=0; i--) {
final PackageSetting ps = mSettings.getPackageLPr(names[i]);
boolean translateName = false;
- if (ps != null && ps.realName != null) {
+ if (ps != null && ps.getRealName() != null) {
final boolean targetIsInstantApp = ps.getInstantApp(callingUserId);
translateName = !targetIsInstantApp
|| canViewInstantApps
|| mInstantAppRegistry.isInstantAccessGranted(callingUserId,
- UserHandle.getAppId(callingUid), ps.appId);
+ UserHandle.getAppId(callingUid), ps.getAppId());
}
- out[i] = translateName ? ps.realName : names[i];
+ out[i] = translateName ? ps.getRealName() : names[i];
}
}
return out;
@@ -3428,7 +2958,7 @@
translateName = !targetIsInstantApp
|| canViewInstantApps
|| mInstantAppRegistry.isInstantAccessGranted(callingUserId,
- UserHandle.getAppId(callingUid), ps.appId);
+ UserHandle.getAppId(callingUid), ps.getAppId());
}
out[i] = translateName ? cur : names[i];
}
@@ -3465,14 +2995,16 @@
final PackageSetting ps = getPackageSetting(p.getPackageName());
if (ps != null && ps.getInstalled(userId)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
- return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
+ return mPermissionManager.getGidsForUid(UserHandle.getUid(userId,
+ ps.getAppId()));
}
}
if ((flags & MATCH_KNOWN_PACKAGES) != 0) {
final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null && ps.isMatch(flags)
&& !shouldFilterApplicationLocked(ps, callingUid, userId)) {
- return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.appId));
+ return mPermissionManager.getGidsForUid(
+ UserHandle.getUid(userId, ps.getAppId()));
}
}
}
@@ -3657,6 +3189,9 @@
if (freeBytesRequired > 0) {
smInternal.freeCache(volumeUuid, freeBytesRequired);
}
+
+ // 12. Clear temp install session files
+ mInstallerService.freeStageDirs(volumeUuid);
} else {
try {
mInstaller.freeCache(volumeUuid, bytes, 0, 0);
@@ -3702,11 +3237,11 @@
final PackageSetting ps = mSettings.getPackageLPr(internalPackageName);
// Skip unused static shared libs cached less than the min period
// to prevent pruning a lib needed by a subsequently installed package.
- if (ps == null || now - ps.lastUpdateTime < maxCachePeriod) {
+ if (ps == null || now - ps.getLastUpdateTime() < maxCachePeriod) {
continue;
}
- if (ps.pkg.isSystem()) {
+ if (ps.getPkg().isSystem()) {
continue;
}
@@ -4056,21 +3591,23 @@
versionedPackages = new ArrayList<>();
}
// If the dependent is a static shared lib, use the public package name
- String dependentPackageName = ps.name;
- if (ps.pkg != null && ps.pkg.isStaticSharedLibrary()) {
- dependentPackageName = ps.pkg.getManifestPackageName();
+ String dependentPackageName = ps.getPackageName();
+ if (ps.getPkg() != null && ps.getPkg().isStaticSharedLibrary()) {
+ dependentPackageName = ps.getPkg().getManifestPackageName();
}
- versionedPackages.add(new VersionedPackage(dependentPackageName, ps.versionCode));
- } else if (ps.pkg != null) {
- if (ArrayUtils.contains(ps.pkg.getUsesLibraries(), libName)
- || ArrayUtils.contains(ps.pkg.getUsesOptionalLibraries(), libName)) {
+ versionedPackages.add(new VersionedPackage(dependentPackageName,
+ ps.getLongVersionCode()));
+ } else if (ps.getPkg() != null) {
+ if (ArrayUtils.contains(ps.getPkg().getUsesLibraries(), libName)
+ || ArrayUtils.contains(ps.getPkg().getUsesOptionalLibraries(), libName)) {
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
continue;
}
if (versionedPackages == null) {
versionedPackages = new ArrayList<>();
}
- versionedPackages.add(new VersionedPackage(ps.name, ps.versionCode));
+ versionedPackages.add(new VersionedPackage(ps.getPackageName(),
+ ps.getLongVersionCode()));
}
}
}
@@ -4208,12 +3745,12 @@
sequenceNumbers = new HashMap<>();
mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers);
}
- final Integer sequenceNumber = sequenceNumbers.get(pkgSetting.name);
+ final Integer sequenceNumber = sequenceNumbers.get(pkgSetting.getPackageName());
if (sequenceNumber != null) {
changedPackages.remove(sequenceNumber);
}
- changedPackages.put(mChangedPackagesSequenceNumber, pkgSetting.name);
- sequenceNumbers.put(pkgSetting.name, mChangedPackagesSequenceNumber);
+ changedPackages.put(mChangedPackagesSequenceNumber, pkgSetting.getPackageName());
+ sequenceNumbers.put(pkgSetting.getPackageName(), mChangedPackagesSequenceNumber);
}
mChangedPackagesSequenceNumber++;
}
@@ -4412,7 +3949,7 @@
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- p1SigningDetails = ps.signatures.mSigningDetails;
+ p1SigningDetails = ps.getSigningDetails();
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
@@ -4432,7 +3969,7 @@
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
- p2SigningDetails = ps.signatures.mSigningDetails;
+ p2SigningDetails = ps.getSigningDetails();
} else {
return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
}
@@ -4526,7 +4063,7 @@
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return false;
}
- signingDetails = ps.signatures.mSigningDetails;
+ signingDetails = ps.getSigningDetails();
} else {
return false;
}
@@ -4593,8 +4130,8 @@
final PackageSetting ps = getPackageSetting(pkg.getPackageName());
if (ps != null
&& ps.getInstantApp(callingUserId)
- && !mInstantAppRegistry.isInstantAccessGranted(
- callingUserId, UserHandle.getAppId(callingUid), ps.appId)) {
+ && !mInstantAppRegistry.isInstantAccessGranted(callingUserId,
+ UserHandle.getAppId(callingUid), ps.getAppId())) {
continue;
}
result.add(pkg.getPackageName());
@@ -4650,7 +4187,7 @@
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
return null;
}
- return ps.name;
+ return ps.getPackageName();
}
return null;
}
@@ -4683,7 +4220,7 @@
if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
names[i] = null;
} else {
- names[i] = ps.name;
+ names[i] = ps.getPackageName();
}
} else {
names[i] = null;
@@ -5739,7 +5276,7 @@
int numMatch = 0;
for (int i=0; i<permissions.length; i++) {
final String permission = permissions[i];
- if (checkPermission(permission, ps.name, userId) == PERMISSION_GRANTED) {
+ if (checkPermission(permission, ps.getPackageName(), userId) == PERMISSION_GRANTED) {
tmp[i] = true;
numMatch++;
} else {
@@ -5838,22 +5375,22 @@
if (ps.isSystem()) {
effectiveFlags |= PackageManager.MATCH_ANY_USER;
}
- if (ps.pkg != null) {
+ if (ps.getPkg() != null) {
if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
continue;
}
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
continue;
}
- ai = PackageInfoUtils.generateApplicationInfo(ps.pkg, effectiveFlags,
+ ai = PackageInfoUtils.generateApplicationInfo(ps.getPkg(), effectiveFlags,
ps.readUserState(userId), userId, ps);
if (ai != null) {
- ai.packageName = resolveExternalPackageNameLPr(ps.pkg);
+ ai.packageName = resolveExternalPackageNameLPr(ps.getPkg());
}
} else {
// Shared lib filtering done in generateApplicationInfoFromSettingsLPw
// and already converts to externally visible package name
- ai = generateApplicationInfoFromSettingsLPw(ps.name,
+ ai = generateApplicationInfoFromSettingsLPw(ps.getPackageName(),
effectiveFlags, callingUid, userId);
}
if (ai != null) {
@@ -6337,7 +5874,7 @@
List<AndroidPackage> pkgs = new ArrayList<>(pkgSettings.size());
for (int index = 0; index < pkgSettings.size(); index++) {
- pkgs.add(pkgSettings.get(index).pkg);
+ pkgs.add(pkgSettings.get(index).getPkg());
}
final long startTime = System.nanoTime();
@@ -6411,11 +5948,11 @@
// Handle compressed APKs in this path. Only do this for stubs with profiles to
// minimize the number off apps being speed-profile compiled during first boot.
// The other paths will not change the filter.
- if (disabledPs != null && disabledPs.pkg.isStub()) {
+ if (disabledPs != null && disabledPs.getPkg().isStub()) {
// The package is the stub one, remove the stub suffix to get the normal
// package and APK names.
- String systemProfilePath =
- getPrebuildProfilePath(disabledPs.pkg).replace(STUB_SUFFIX, "");
+ String systemProfilePath = getPrebuildProfilePath(disabledPs.getPkg())
+ .replace(STUB_SUFFIX, "");
profileFile = new File(systemProfilePath);
// If we have a profile for a compressed APK, copy it to the reference
// location.
@@ -6651,10 +6188,6 @@
}
}
- /*package*/ void controlDexOptBlocking(boolean block) {
- mPackageDexOptimizer.controlDexOptBlocking(block);
- }
-
/**
* Perform dexopt on the given package and return one of following result:
* {@link PackageDexOptimizer#DEX_OPT_SKIPPED}
@@ -6843,7 +6376,7 @@
for (SharedLibraryInfo info : deps) {
PackageSetting depPackageSetting =
mSettings.getPackageLPr(info.getPackageName());
- if (depPackageSetting != null && depPackageSetting.pkg != null) {
+ if (depPackageSetting != null && depPackageSetting.getPkg() != null) {
retValue.add(depPackageSetting);
}
}
@@ -7175,7 +6708,7 @@
Map<String, AndroidPackage> availablePackages)
throws PackageManagerException {
final ArrayList<SharedLibraryInfo> sharedLibraryInfos = collectSharedLibraryInfos(
- pkgSetting.pkg, availablePackages, mSharedLibraries, null /* newLibraries */,
+ pkgSetting.getPkg(), availablePackages, mSharedLibraries, null /* newLibraries */,
mInjector.getCompatibility());
executeSharedLibrariesUpdateLPr(pkg, pkgSetting, changingLib, changingLibSetting,
sharedLibraryInfos, mUserManager.getUserIds());
@@ -7479,10 +7012,10 @@
final String realPkgName = request.mRealPkgName;
final List<String> changedAbiCodePath = result.mChangedAbiCodePath;
final PackageSetting pkgSetting;
- if (request.mPkgSetting != null && request.mPkgSetting.sharedUser != null
- && request.mPkgSetting.sharedUser != result.mPkgSetting.sharedUser) {
+ if (request.mPkgSetting != null && request.mPkgSetting.getSharedUser() != null
+ && request.mPkgSetting.getSharedUser() != result.mPkgSetting.getSharedUser()) {
// shared user changed, remove from old shared user
- request.mPkgSetting.sharedUser.removePackage(request.mPkgSetting);
+ request.mPkgSetting.getSharedUser().removePackage(request.mPkgSetting);
}
if (result.mExistingSettingCopied) {
pkgSetting = request.mPkgSetting;
@@ -7492,18 +7025,18 @@
if (originalPkgSetting != null) {
mSettings.addRenamedPackageLPw(
AndroidPackageUtils.getRealPackageOrNull(parsedPackage),
- originalPkgSetting.name);
- mTransferredPackages.add(originalPkgSetting.name);
+ originalPkgSetting.getPackageName());
+ mTransferredPackages.add(originalPkgSetting.getPackageName());
} else {
mSettings.removeRenamedPackageLPw(parsedPackage.getPackageName());
}
}
- if (pkgSetting.sharedUser != null) {
- pkgSetting.sharedUser.addPackage(pkgSetting);
+ if (pkgSetting.getSharedUser() != null) {
+ pkgSetting.getSharedUser().addPackage(pkgSetting);
}
if (reconciledPkg.mInstallArgs != null
- && reconciledPkg.mInstallArgs.mForceQueryableOverride) {
- pkgSetting.forceQueryableOverride = true;
+ && reconciledPkg.mInstallArgs.mForceQueryableOverride) {
+ pkgSetting.setForceQueryableOverride(true);
}
// If this is part of a standard install, set the initiating package name, else rely on
@@ -7515,7 +7048,7 @@
installSource.initiatingPackageName);
if (ips != null) {
installSource = installSource.setInitiatingPackageSignatures(
- ips.signatures);
+ ips.getSignatures());
}
}
pkgSetting.setInstallSource(installSource);
@@ -7527,7 +7060,7 @@
// We need to have this here because addUserToSettingLPw() is sometimes responsible
// for creating the application ID. If we did this earlier, we would be saving the
// correct ID.
- parsedPackage.setUid(pkgSetting.appId);
+ parsedPackage.setUid(pkgSetting.getAppId());
final AndroidPackage pkg = parsedPackage.hideAsFinal();
mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
@@ -7546,10 +7079,10 @@
ksms.removeAppKeySetDataLPw(pkg.getPackageName());
}
if (reconciledPkg.mSharedUserSignaturesChanged) {
- pkgSetting.sharedUser.signaturesChanged = Boolean.TRUE;
- pkgSetting.sharedUser.signatures.mSigningDetails = reconciledPkg.mSigningDetails;
+ pkgSetting.getSharedUser().signaturesChanged = Boolean.TRUE;
+ pkgSetting.getSharedUser().signatures.mSigningDetails = reconciledPkg.mSigningDetails;
}
- pkgSetting.signatures.mSigningDetails = reconciledPkg.mSigningDetails;
+ pkgSetting.setSigningDetails(reconciledPkg.mSigningDetails);
if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
@@ -7567,7 +7100,7 @@
commitPackageSettings(pkg, oldPkg, pkgSetting, oldPkgSetting, scanFlags,
(parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
if (pkgSetting.getInstantApp(userId)) {
- mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
+ mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.getAppId());
}
pkgSetting.setStatesOnCommit();
@@ -7935,7 +7468,7 @@
// Remove the parent package setting
PackageSetting ps = getPackageSetting(pkg.getPackageName());
if (ps != null) {
- removePackageLI(ps.name, chatty);
+ removePackageLI(ps.getPackageName(), chatty);
} else if (DEBUG_REMOVE && chatty) {
Log.d(TAG, "Not removing package " + pkg.getPackageName() + "; mExtras == null");
}
@@ -8027,25 +7560,9 @@
final int[] userIds, int[] instantUserIds,
@Nullable SparseArray<int[]> broadcastAllowList,
@Nullable Bundle bOptions) {
- mHandler.post(() -> {
- try {
- final IActivityManager am = ActivityManager.getService();
- if (am == null) return;
- final int[] resolvedUserIds;
- if (userIds == null) {
- resolvedUserIds = am.getRunningUserIds();
- } else {
- resolvedUserIds = userIds;
- }
- doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
- resolvedUserIds, false, broadcastAllowList, bOptions);
- if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
- doSendBroadcast(action, pkg, extras, flags, targetPkg, finishedReceiver,
- instantUserIds, true, null, bOptions);
- }
- } catch (RemoteException ex) {
- }
- });
+ mHandler.post(() -> mBroadcastHelper.sendPackageBroadcast(action, pkg, extras, flags,
+ targetPkg, finishedReceiver, userIds, instantUserIds, broadcastAllowList,
+ bOptions));
}
@Override
@@ -8102,54 +7619,6 @@
}
}
- /**
- * Sends a broadcast for the given action.
- * <p>If {@code isInstantApp} is {@code true}, then the broadcast is protected with
- * the {@link android.Manifest.permission#ACCESS_INSTANT_APPS} permission. This allows
- * the system and applications allowed to see instant applications to receive package
- * lifecycle events for instant applications.
- */
- private void doSendBroadcast(String action, String pkg, Bundle extras,
- int flags, String targetPkg, IIntentReceiver finishedReceiver,
- int[] userIds, boolean isInstantApp, @Nullable SparseArray<int[]> broadcastAllowList,
- @Nullable Bundle bOptions) {
- for (int id : userIds) {
- final Intent intent = new Intent(action,
- pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
- final String[] requiredPermissions =
- isInstantApp ? INSTANT_APP_BROADCAST_PERMISSION : null;
- if (extras != null) {
- intent.putExtras(extras);
- }
- if (targetPkg != null) {
- intent.setPackage(targetPkg);
- }
- // Modify the UID when posting to other users
- int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- if (uid > 0 && UserHandle.getUserId(uid) != id) {
- uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
- intent.putExtra(Intent.EXTRA_UID, uid);
- }
- if (broadcastAllowList != null && PLATFORM_PACKAGE_NAME.equals(targetPkg)) {
- intent.putExtra(Intent.EXTRA_VISIBILITY_ALLOW_LIST, broadcastAllowList.get(id));
- }
- intent.putExtra(Intent.EXTRA_USER_HANDLE, id);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
- if (DEBUG_BROADCASTS) {
- RuntimeException here = new RuntimeException("here");
- here.fillInStackTrace();
- Slog.d(TAG, "Sending to user " + id + ": "
- + intent.toShortString(false, true, false, false)
- + " " + intent.getExtras(), here);
- }
- mInjector.getLocalService(ActivityManagerInternal.class).broadcastIntent(
- intent, finishedReceiver, requiredPermissions,
- finishedReceiver != null, id,
- broadcastAllowList == null ? null : broadcastAllowList.get(id),
- bOptions);
- }
- }
-
private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting,
int userId, int dataLoaderType) {
final boolean isSystem = PackageManagerServiceUtils.isSystemApp(pkgSetting)
@@ -8158,7 +7627,7 @@
final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
sendPackageAddedForNewUsers(packageName, isSystem /*sendBootCompleted*/,
- false /*startReceiver*/, pkgSetting.appId, userIds, instantUserIds,
+ false /*startReceiver*/, pkgSetting.getAppId(), userIds, instantUserIds,
dataLoaderType);
// Send a session commit broadcast
@@ -8175,22 +7644,15 @@
if (ArrayUtils.isEmpty(userIds) && ArrayUtils.isEmpty(instantUserIds)) {
return;
}
- Bundle extras = new Bundle(1);
- // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast
- final int uid = UserHandle.getUid(
- (ArrayUtils.isEmpty(userIds) ? instantUserIds[0] : userIds[0]), appId);
- extras.putInt(Intent.EXTRA_UID, uid);
- extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
-
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, 0, null, null, userIds, instantUserIds,
- mAppsFilter.getVisibilityAllowList(
- getPackageSettingInternal(packageName, Process.SYSTEM_UID),
- userIds, mSettings.getPackagesLocked()), null);
+ SparseArray<int[]> broadcastAllowList = mAppsFilter.getVisibilityAllowList(
+ getPackageSettingInternal(packageName, Process.SYSTEM_UID),
+ userIds, mSettings.getPackagesLocked());
+ mHandler.post(() -> mBroadcastHelper.sendPackageAddedForNewUsers(
+ packageName, appId, userIds, instantUserIds, dataLoaderType, broadcastAllowList));
if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) {
mHandler.post(() -> {
for (int userId : userIds) {
- sendBootCompletedBroadcastToSystemApp(
+ mBroadcastHelper.sendBootCompletedBroadcastToSystemApp(
packageName, includeStopped, userId);
}
}
@@ -8198,48 +7660,6 @@
}
}
- /**
- * The just-installed/enabled app is bundled on the system, so presumed to be able to run
- * automatically without needing an explicit launch.
- * Send it a LOCKED_BOOT_COMPLETED/BOOT_COMPLETED if it would ordinarily have gotten ones.
- */
- private void sendBootCompletedBroadcastToSystemApp(
- String packageName, boolean includeStopped, int userId) {
- // If user is not running, the app didn't miss any broadcast
- if (!mUserManager.isUserRunning(userId)) {
- return;
- }
- final IActivityManager am = ActivityManager.getService();
- try {
- // Deliver LOCKED_BOOT_COMPLETED first
- Intent lockedBcIntent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
- .setPackage(packageName);
- if (includeStopped) {
- lockedBcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
- }
- final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED};
- final BroadcastOptions bOptions = getTemporaryAppAllowlistBroadcastOptions(
- REASON_LOCKED_BOOT_COMPLETED);
- am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null,
- requiredPermissions, null, android.app.AppOpsManager.OP_NONE,
- bOptions.toBundle(), false, false, userId);
-
- // Deliver BOOT_COMPLETED only if user is unlocked
- final UserManagerInternal umInternal = mInjector.getUserManagerInternal();
- if (umInternal.isUserUnlockingOrUnlocked(userId)) {
- Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED).setPackage(packageName);
- if (includeStopped) {
- bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
- }
- am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null,
- requiredPermissions, null, android.app.AppOpsManager.OP_NONE,
- bOptions.toBundle(), false, false, userId);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
@Override
public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
int userId) {
@@ -8283,7 +7703,7 @@
return false;
}
// Only allow protected packages to hide themselves.
- if (hidden && !UserHandle.isSameApp(callingUid, pkgSetting.appId)
+ if (hidden && !UserHandle.isSameApp(callingUid, pkgSetting.getAppId())
&& mProtectedPackages.isPackageStateProtected(userId, packageName)) {
Slog.w(TAG, "Not hiding protected package: " + packageName);
return false;
@@ -8304,7 +7724,7 @@
return true;
}
if (sendRemoved) {
- killApplication(packageName, pkgSetting.appId, userId,
+ killApplication(packageName, pkgSetting.getAppId(), userId,
"hiding pkg");
sendApplicationHiddenForUser(packageName, pkgSetting, userId);
return true;
@@ -8396,24 +7816,13 @@
int userId) {
final PackageRemovedInfo info = new PackageRemovedInfo(this);
info.mRemovedPackage = packageName;
- info.mInstallerPackageName = pkgSetting.installSource.installerPackageName;
+ info.mInstallerPackageName = pkgSetting.getInstallSource().installerPackageName;
info.mRemovedUsers = new int[] {userId};
info.mBroadcastUsers = new int[] {userId};
- info.mUid = UserHandle.getUid(userId, pkgSetting.appId);
+ info.mUid = UserHandle.getUid(userId, pkgSetting.getAppId());
info.sendPackageRemovedBroadcasts(true /*killApp*/, false /*removedBySystem*/);
}
- private void sendDistractingPackagesChanged(String[] pkgList, int[] uidList, int userId,
- int distractionFlags) {
- final Bundle extras = new Bundle(3);
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
- extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
- extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
- sendPackageBroadcast(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null, extras,
- Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, new int[]{userId}, null, null,
- null);
- }
-
@VisibleForTesting(visibility = Visibility.PRIVATE)
void sendPackagesSuspendedForUser(String intent, String[] pkgList, int[] uidList, int userId) {
final List<List<String>> pkgsToSend = new ArrayList(pkgList.length);
@@ -8571,24 +7980,24 @@
}
if (installed) {
- if (pkgSetting.pkg != null) {
+ if (pkgSetting.getPkg() != null) {
final PermissionManagerServiceInternal.PackageInstalledParams.Builder
permissionParamsBuilder =
new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS)
!= 0) {
permissionParamsBuilder.setAllowlistedRestrictedPermissions(
- pkgSetting.pkg.getRequestedPermissions());
+ pkgSetting.getPkg().getRequestedPermissions());
}
- mPermissionManager.onPackageInstalled(pkgSetting.pkg,
+ mPermissionManager.onPackageInstalled(pkgSetting.getPkg(),
Process.INVALID_UID /* previousAppId */,
permissionParamsBuilder.build(), userId);
}
- if (pkgSetting.pkg != null) {
+ if (pkgSetting.getPkg() != null) {
synchronized (mInstallLock) {
// We don't need to freeze for a brand new install
- prepareAppDataAfterInstallLIF(pkgSetting.pkg);
+ prepareAppDataAfterInstallLIF(pkgSetting.getPkg());
}
}
sendPackageAddedForUser(packageName, pkgSetting, userId, DataLoaderType.NONE);
@@ -8598,7 +8007,7 @@
// start async restore with no post-install since we finish install here
PackageInstalledInfo res = new PackageInstalledInfo(
PackageManager.INSTALL_SUCCEEDED);
- res.mPkg = pkgSetting.pkg;
+ res.mPkg = pkgSetting.getPkg();
res.mNewUsers = new int[]{ userId };
PostInstallData postInstallData =
@@ -8628,8 +8037,8 @@
}
}
- static void setInstantAppForUser(Injector injector, PackageSetting pkgSetting,
- int userId, boolean instantApp, boolean fullApp) {
+ static void setInstantAppForUser(PackageManagerServiceInjector injector,
+ PackageSetting pkgSetting, int userId, boolean instantApp, boolean fullApp) {
// no state specified; do nothing
if (!instantApp && !fullApp) {
return;
@@ -8706,7 +8115,7 @@
if (restrictionFlags != oldDistractionFlags) {
pkgSetting.setDistractionFlags(restrictionFlags, userId);
changedPackagesList.add(packageName);
- changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
+ changedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
}
}
}
@@ -8714,8 +8123,8 @@
if (!changedPackagesList.isEmpty()) {
final String[] changedPackages = changedPackagesList.toArray(
new String[changedPackagesList.size()]);
- sendDistractingPackagesChanged(changedPackages, changedUids.toArray(), userId,
- restrictionFlags);
+ mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged(
+ changedPackages, changedUids.toArray(), userId, restrictionFlags));
synchronized (mLock) {
scheduleWritePackageRestrictionsLocked(userId);
}
@@ -8814,11 +8223,11 @@
}
if (suspended || packageUnsuspended) {
changedPackagesList.add(packageName);
- changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
+ changedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
}
if (packageModified) {
modifiedPackagesList.add(packageName);
- modifiedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
+ modifiedUids.add(UserHandle.getUid(userId, pkgSetting.getAppId()));
}
}
@@ -8895,9 +8304,9 @@
} else {
intentExtras = null;
}
- doSendBroadcast(action, null, intentExtras,
+ mHandler.post(() -> mBroadcastHelper.doSendBroadcast(action, null, intentExtras,
Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
- targetUserIds, false, null, null);
+ targetUserIds, false, null, null));
}
});
}
@@ -8916,7 +8325,7 @@
}
}
- void unsuspendForSuspendingPackage(String suspendingPackage, int userId) {
+ private void unsuspendForSuspendingPackage(String suspendingPackage, int userId) {
final String[] allPackages;
synchronized (mLock) {
allPackages = mPackages.keySet().toArray(new String[mPackages.size()]);
@@ -8924,7 +8333,7 @@
removeSuspensionsBySuspendingPackage(allPackages, suspendingPackage::equals, userId);
}
- boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
+ private boolean isSuspendingAnyPackages(String suspendingPackage, int userId) {
synchronized (mLock) {
for (final PackageSetting ps : mSettings.getPackagesLocked().values()) {
if (ps.isSuspendedBy(suspendingPackage, userId)) {
@@ -8946,7 +8355,7 @@
* suspensions will be removed.
* @param userId The user for which the changes are taking place.
*/
- void removeSuspensionsBySuspendingPackage(String[] packagesToChange,
+ private void removeSuspensionsBySuspendingPackage(String[] packagesToChange,
Predicate<String> suspendingPackagePredicate, int userId) {
final List<String> unsuspendedPackages = new ArrayList<>();
final IntArray unsuspendedUids = new IntArray();
@@ -8956,7 +8365,7 @@
if (ps != null && ps.getSuspended(userId)) {
ps.removeSuspension(suspendingPackagePredicate, userId);
if (!ps.getSuspended(userId)) {
- unsuspendedPackages.add(ps.name);
+ unsuspendedPackages.add(ps.getPackageName());
unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId()));
}
}
@@ -8972,7 +8381,7 @@
}
}
- void removeAllDistractingPackageRestrictions(int userId) {
+ private void removeAllDistractingPackageRestrictions(int userId) {
final String[] allPackages;
synchronized (mLock) {
allPackages = mPackages.keySet().toArray(new String[mPackages.size()]);
@@ -8989,7 +8398,7 @@
* @param packagesToChange The packages on which restrictions are to be removed.
* @param userId the user for which changes are taking place.
*/
- void removeDistractingPackageRestrictions(String[] packagesToChange, int userId) {
+ private void removeDistractingPackageRestrictions(String[] packagesToChange, int userId) {
final List<String> changedPackages = new ArrayList<>();
final IntArray changedUids = new IntArray();
synchronized (mLock) {
@@ -8997,14 +8406,15 @@
final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps != null && ps.getDistractionFlags(userId) != 0) {
ps.setDistractionFlags(0, userId);
- changedPackages.add(ps.name);
+ changedPackages.add(ps.getPackageName());
changedUids.add(UserHandle.getUid(userId, ps.getAppId()));
}
}
if (!changedPackages.isEmpty()) {
final String[] packageArray = changedPackages.toArray(
new String[changedPackages.size()]);
- sendDistractingPackagesChanged(packageArray, changedUids.toArray(), userId, 0);
+ mHandler.post(() -> mBroadcastHelper.sendDistractingPackagesChanged(
+ packageArray, changedUids.toArray(), userId, 0));
scheduleWritePackageRestrictionsLocked(userId);
}
}
@@ -9320,7 +8730,7 @@
((SharedUserSetting) obj).signatures.mSigningDetails.getSignatures();
} else if (obj instanceof PackageSetting) {
callerSignature =
- ((PackageSetting) obj).signatures.mSigningDetails.getSignatures();
+ ((PackageSetting) obj).getSigningDetails().getSignatures();
} else {
throw new SecurityException("Bad object " + obj + " for uid " + callingUid);
}
@@ -9332,7 +8742,7 @@
// not signed with the same cert as the caller.
if (installerPackageSetting != null) {
if (compareSignatures(callerSignature,
- installerPackageSetting.signatures.mSigningDetails.getSignatures())
+ installerPackageSetting.getSigningDetails().getSignatures())
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as new installer package "
@@ -9343,13 +8753,13 @@
// Verify: if target already has an installer package, it must
// be signed with the same cert as the caller.
String targetInstallerPackageName =
- targetPackageSetting.installSource.installerPackageName;
+ targetPackageSetting.getInstallSource().installerPackageName;
PackageSetting targetInstallerPkgSetting = targetInstallerPackageName == null ? null :
mSettings.getPackageLPr(targetInstallerPackageName);
if (targetInstallerPkgSetting != null) {
if (compareSignatures(callerSignature,
- targetInstallerPkgSetting.signatures.mSigningDetails.getSignatures())
+ targetInstallerPkgSetting.getSigningDetails().getSignatures())
!= PackageManager.SIGNATURE_MATCH) {
throw new SecurityException(
"Caller does not have same cert as old installer package "
@@ -9381,7 +8791,7 @@
// Okay!
targetPackageSetting.setInstallerPackageName(installerPackageName);
- mSettings.addInstallerPackageNames(targetPackageSetting.installSource);
+ mSettings.addInstallerPackageNames(targetPackageSetting.getInstallSource());
mAppsFilter.addPackage(targetPackageSetting);
scheduleWriteSettingsLocked();
}
@@ -9401,13 +8811,13 @@
ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) {
throw new IllegalArgumentException("Unknown target package " + packageName);
}
- if (!Objects.equals(callerPackageName, ps.installSource.installerPackageName)) {
+ if (!Objects.equals(callerPackageName, ps.getInstallSource().installerPackageName)) {
throw new IllegalArgumentException("Calling package " + callerPackageName
+ " is not installer for " + packageName);
}
- if (ps.categoryHint != categoryHint) {
- ps.categoryHint = categoryHint;
+ if (ps.getCategoryOverride() != categoryHint) {
+ ps.setCategoryOverride(categoryHint);
scheduleWriteSettingsLocked();
}
}
@@ -9529,7 +8939,7 @@
synchronized (mLock) {
ps = mSettings.getPackageLPr(packageName);
if (ps != null) {
- appId = ps.appId;
+ appId = ps.getAppId();
ceDataInode = ps.getCeDataInode(userId);
}
@@ -9594,16 +9004,11 @@
final boolean isInstantApp = isInstantApp(packageName, userId);
final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
- sendFirstLaunchBroadcast(packageName, installerPackage, userIds, instantUserIds);
+ mBroadcastHelper.sendFirstLaunchBroadcast(
+ packageName, installerPackage, userIds, instantUserIds);
});
}
- void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
- int[] userIds, int[] instantUserIds) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
- installerPkg, null, userIds, instantUserIds, null /* broadcastAllowList */, null);
- }
-
/**
* Create args that describe an existing installed package. Typically used
* when cleaning up old installs, or used as a move source.
@@ -9614,7 +9019,8 @@
@GuardedBy("mLock")
Map<String, ReconciledPackage> reconcilePackagesLocked(
- final ReconcileRequest request, KeySetManagerService ksms, Injector injector)
+ final ReconcileRequest request, KeySetManagerService ksms,
+ PackageManagerServiceInjector injector)
throws ReconcileFailure {
final Map<String, ScanResult> scannedPackages = request.mScannedPackages;
@@ -9633,7 +9039,8 @@
final ScanResult scanResult = scannedPackages.get(installPackageName);
// add / replace existing with incoming packages
- combinedPackages.put(scanResult.mPkgSetting.name, scanResult.mRequest.mParsedPackage);
+ combinedPackages.put(scanResult.mPkgSetting.getPackageName(),
+ scanResult.mRequest.mParsedPackage);
// in the first pass, we'll build up the set of incoming shared libraries
final List<SharedLibraryInfo> allowedSharedLibInfos =
@@ -9729,20 +9136,21 @@
// newer
// signing certificate than the existing one, and if so, copy over the new
// details
- if (signatureCheckPs.sharedUser != null) {
+ if (signatureCheckPs.getSharedUser() != null) {
// Attempt to merge the existing lineage for the shared SigningDetails with
// the lineage of the new package; if the shared SigningDetails are not
// returned this indicates the new package added new signers to the lineage
// and/or changed the capabilities of existing signers in the lineage.
SigningDetails sharedSigningDetails =
- signatureCheckPs.sharedUser.signatures.mSigningDetails;
+ signatureCheckPs.getSharedUser().signatures.mSigningDetails;
SigningDetails mergedDetails = sharedSigningDetails.mergeLineageWith(
signingDetails);
if (mergedDetails != sharedSigningDetails) {
- signatureCheckPs.sharedUser.signatures.mSigningDetails = mergedDetails;
+ signatureCheckPs.getSharedUser().signatures.mSigningDetails =
+ mergedDetails;
}
- if (signatureCheckPs.sharedUser.signaturesChanged == null) {
- signatureCheckPs.sharedUser.signaturesChanged = Boolean.FALSE;
+ if (signatureCheckPs.getSharedUser().signaturesChanged == null) {
+ signatureCheckPs.getSharedUser().signaturesChanged = Boolean.FALSE;
}
}
} catch (PackageManagerException e) {
@@ -9759,10 +9167,10 @@
// updating
// the signatures on the first package scanned for the shared user (i.e. if the
// signaturesChanged state hasn't been initialized yet in SharedUserSetting).
- if (signatureCheckPs.sharedUser != null) {
- final Signature[] sharedUserSignatures = signatureCheckPs.sharedUser
+ if (signatureCheckPs.getSharedUser() != null) {
+ final Signature[] sharedUserSignatures = signatureCheckPs.getSharedUser()
.signatures.mSigningDetails.getSignatures();
- if (signatureCheckPs.sharedUser.signaturesChanged != null
+ if (signatureCheckPs.getSharedUser().signaturesChanged != null
&& compareSignatures(sharedUserSignatures,
parsedPackage.getSigningDetails().getSignatures())
!= PackageManager.SIGNATURE_MATCH) {
@@ -9776,7 +9184,7 @@
throw new ReconcileFailure(
INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
"Signature mismatch for shared user: "
- + scanResult.mPkgSetting.sharedUser);
+ + scanResult.mPkgSetting.getSharedUser());
} else {
// Treat mismatched signatures on system packages using a shared
// UID as
@@ -9786,14 +9194,14 @@
"Signature mismatch on system package "
+ parsedPackage.getPackageName()
+ " for shared user "
- + scanResult.mPkgSetting.sharedUser);
+ + scanResult.mPkgSetting.getSharedUser());
}
}
sharedUserSignaturesChanged = true;
- signatureCheckPs.sharedUser.signatures.mSigningDetails =
+ signatureCheckPs.getSharedUser().signatures.mSigningDetails =
parsedPackage.getSigningDetails();
- signatureCheckPs.sharedUser.signaturesChanged = Boolean.TRUE;
+ signatureCheckPs.getSharedUser().signaturesChanged = Boolean.TRUE;
}
// File a report about this.
String msg = "System package " + parsedPackage.getPackageName()
@@ -9875,8 +9283,8 @@
? scanResult.mRequest.mOldPkgSetting
: scanResult.mRequest.mDisabledPkgSetting
: null;
- if (isUpdatedSystemApp && (updatedSystemPs.pkg == null
- || updatedSystemPs.pkg.getLibraryNames() == null)) {
+ if (isUpdatedSystemApp && (updatedSystemPs.getPkg() == null
+ || updatedSystemPs.getPkg().getLibraryNames() == null)) {
Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ " declares libraries that are not declared on the system image; skipping");
return null;
@@ -9896,7 +9304,7 @@
// with it. Better to just have the restriction here, be
// conservative, and create many fewer cases that can negatively
// impact the user experience.
- if (!updatedSystemPs.pkg.getLibraryNames().contains(name)) {
+ if (!updatedSystemPs.getPkg().getLibraryNames().contains(name)) {
Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ " declares library " + name
+ " that is not declared on system image; skipping");
@@ -10356,9 +9764,9 @@
}
if (versionCode != PackageManager.VERSION_CODE_HIGHEST
- && uninstalledPs.versionCode != versionCode) {
+ && uninstalledPs.getLongVersionCode() != versionCode) {
Slog.w(TAG, "Not removing package " + packageName + " with versionCode "
- + uninstalledPs.versionCode + " != " + versionCode);
+ + uninstalledPs.getLongVersionCode() + " != " + versionCode);
return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}
@@ -10468,7 +9876,7 @@
}
final AndroidPackage stubPkg =
- (disabledSystemPs == null) ? null : disabledSystemPs.pkg;
+ (disabledSystemPs == null) ? null : disabledSystemPs.getPkg();
if (stubPkg != null && stubPkg.isStub()) {
final PackageSetting stubPs;
synchronized (mLock) {
@@ -10502,20 +9910,20 @@
*/
void removePackageDataLIF(final PackageSetting deletedPs, @NonNull int[] allUserHandles,
PackageRemovedInfo outInfo, int flags, boolean writeSettings) {
- String packageName = deletedPs.name;
+ String packageName = deletedPs.getPackageName();
if (DEBUG_REMOVE) Slog.d(TAG, "removePackageDataLI: " + deletedPs);
// Retrieve object to delete permissions for shared user later on
- final AndroidPackage deletedPkg = deletedPs.pkg;
+ final AndroidPackage deletedPkg = deletedPs.getPkg();
if (outInfo != null) {
outInfo.mRemovedPackage = packageName;
- outInfo.mInstallerPackageName = deletedPs.installSource.installerPackageName;
+ outInfo.mInstallerPackageName = deletedPs.getInstallSource().installerPackageName;
outInfo.mIsStaticSharedLib = deletedPkg != null
&& deletedPkg.getStaticSharedLibName() != null;
outInfo.populateUsers(deletedPs == null ? null
: deletedPs.queryInstalledUsers(mUserManager.getUserIds(), true), deletedPs);
}
- removePackageLI(deletedPs.name, (flags & PackageManager.DELETE_CHATTY) != 0);
+ removePackageLI(deletedPs.getPackageName(), (flags & PackageManager.DELETE_CHATTY) != 0);
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
final AndroidPackage resolvedPkg;
@@ -10524,8 +9932,8 @@
} else {
// We don't have a parsed package when it lives on an ejected
// adopted storage device, so fake something together
- resolvedPkg = PackageImpl.buildFakeForDeletion(deletedPs.name,
- deletedPs.volumeUuid);
+ resolvedPkg = PackageImpl.buildFakeForDeletion(deletedPs.getPackageName(),
+ deletedPs.getVolumeUuid());
}
destroyAppDataLIF(resolvedPkg, UserHandle.USER_ALL,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
@@ -10543,7 +9951,7 @@
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
final SparseBooleanArray changedUsers = new SparseBooleanArray();
synchronized (mLock) {
- mDomainVerificationManager.clearPackage(deletedPs.name);
+ mDomainVerificationManager.clearPackage(deletedPs.getPackageName());
mSettings.getKeySetManagerService().removeAppKeySetDataLPw(packageName);
mAppsFilter.removePackage(getPackageSetting(packageName),
false /* isReplace */);
@@ -10560,13 +9968,13 @@
if (sharedUserPkgs == null) {
sharedUserPkgs = Collections.emptyList();
}
- mPermissionManager.onPackageUninstalled(packageName, deletedPs.appId,
- deletedPs.pkg, sharedUserPkgs, UserHandle.USER_ALL);
+ mPermissionManager.onPackageUninstalled(packageName, deletedPs.getAppId(),
+ deletedPs.getPkg(), sharedUserPkgs, UserHandle.USER_ALL);
}
clearPackagePreferredActivitiesLPw(
- deletedPs.name, changedUsers, UserHandle.USER_ALL);
+ deletedPs.getPackageName(), changedUsers, UserHandle.USER_ALL);
- mSettings.removeRenamedPackageLPw(deletedPs.realName);
+ mSettings.removeRenamedPackageLPw(deletedPs.getRealName());
}
if (changedUsers.size() > 0) {
updateDefaultHomeNotLocked(changedUsers);
@@ -10629,7 +10037,7 @@
PackageRemovedInfo outInfo, boolean writeSettings) {
synchronized (mLock) {
if (outInfo != null) {
- outInfo.mUid = ps.appId;
+ outInfo.mUid = ps.getAppId();
outInfo.mBroadcastAllowList = mAppsFilter.getVisibilityAllowList(ps,
allUserHandles, mSettings.getPackagesLocked());
}
@@ -10642,7 +10050,7 @@
if (deleteCodeAndResources && (outInfo != null)) {
outInfo.mArgs = createInstallArgsForExisting(
ps.getPathString(), getAppDexInstructionSets(
- ps.primaryCpuAbiString, ps.secondaryCpuAbiString));
+ ps.getPrimaryCpuAbi(), ps.getSecondaryCpuAbi()));
if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.mArgs);
}
}
@@ -10716,7 +10124,8 @@
final boolean deleteAllUsers =
user == null || user.getIdentifier() == UserHandle.USER_ALL;
if ((!deleteSystem || deleteAllUsers) && disabledPs == null) {
- Slog.w(TAG, "Attempt to delete unknown system package " + ps.pkg.getPackageName());
+ Slog.w(TAG, "Attempt to delete unknown system package "
+ + ps.getPkg().getPackageName());
return null;
}
// Confirmed if the system package has been updated
@@ -10820,14 +10229,14 @@
// TODO(b/109941548): break reasons for ret = false out into mayDelete method
if (systemApp) {
- if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.name);
+ if (DEBUG_REMOVE) Slog.d(TAG, "Removing system package: " + ps.getPackageName());
// When an updated system application is deleted we delete the existing resources
// as well and fall back to existing code in system partition
final InitAndSystemPackageHelper helper = new InitAndSystemPackageHelper(this);
helper.deleteSystemPackageLIF(action, ps, allUserHandles, flags, outInfo,
writeSettings, mDefParseFlags, mDirsToScanAsSystem);
} else {
- if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
+ if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.getPackageName());
deleteInstalledPackageLIF(ps, deleteCodeAndResources, flags, allUserHandles,
outInfo, writeSettings);
}
@@ -10847,7 +10256,7 @@
// Take a note whether we deleted the package for all users
if (outInfo != null) {
- outInfo.mRemovedForAllUsers = mPackages.get(ps.name) == null;
+ outInfo.mRemovedForAllUsers = mPackages.get(ps.getPackageName()) == null;
}
}
@@ -10857,7 +10266,8 @@
? mUserManager.getUserIds() : new int[] {user.getIdentifier()};
for (int nextUserId : userIds) {
if (DEBUG_REMOVE) {
- Slog.d(TAG, "Marking package:" + ps.name + " uninstalled for user:" + nextUserId);
+ Slog.d(TAG, "Marking package:" + ps.getPackageName()
+ + " uninstalled for user:" + nextUserId);
}
ps.setUserState(nextUserId, 0, COMPONENT_ENABLED_STATE_DEFAULT,
false /*installed*/,
@@ -10884,7 +10294,7 @@
PackageRemovedInfo outInfo, int flags) {
final AndroidPackage pkg;
synchronized (mLock) {
- pkg = mPackages.get(ps.name);
+ pkg = mPackages.get(ps.getPackageName());
}
destroyAppProfilesLIF(pkg);
@@ -10898,27 +10308,29 @@
: new int[] {userId};
for (int nextUserId : userIds) {
if (DEBUG_REMOVE) {
- Slog.d(TAG, "Updating package:" + ps.name + " install state for user:"
+ Slog.d(TAG, "Updating package:" + ps.getPackageName() + " install state for user:"
+ nextUserId);
}
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
destroyAppDataLIF(pkg, nextUserId,
FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);
}
- removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), nextUserId, ps.appId);
- clearPackagePreferredActivities(ps.name, nextUserId);
- mDomainVerificationManager.clearPackageForUser(ps.name, nextUserId);
+ removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), nextUserId,
+ ps.getAppId());
+ clearPackagePreferredActivities(ps.getPackageName(), nextUserId);
+ mDomainVerificationManager.clearPackageForUser(ps.getPackageName(), nextUserId);
}
- mPermissionManager.onPackageUninstalled(ps.name, ps.appId, pkg, sharedUserPkgs, userId);
+ mPermissionManager.onPackageUninstalled(ps.getPackageName(), ps.getAppId(), pkg,
+ sharedUserPkgs, userId);
if (outInfo != null) {
if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
outInfo.mDataRemoved = true;
}
- outInfo.mRemovedPackage = ps.name;
- outInfo.mInstallerPackageName = ps.installSource.installerPackageName;
+ outInfo.mRemovedPackage = ps.getPackageName();
+ outInfo.mInstallerPackageName = ps.getInstallSource().installerPackageName;
outInfo.mIsStaticSharedLib = pkg != null && pkg.getStaticSharedLibName() != null;
- outInfo.mRemovedAppId = ps.appId;
+ outInfo.mRemovedAppId = ps.getAppId();
outInfo.mRemovedUsers = userIds;
outInfo.mBroadcastUsers = userIds;
}
@@ -11017,7 +10429,7 @@
ps = mSettings.getPackageLPr(packageName);
if (pkg == null) {
if (ps != null) {
- pkg = ps.pkg;
+ pkg = ps.getPkg();
}
}
}
@@ -11158,16 +10570,16 @@
final int numPackages = sus.packages.size();
for (int index = 0; index < numPackages; index++) {
final PackageSetting ps = sus.packages.valueAt(index);
- if (ps.pkg != null) {
- int v = ps.pkg.getTargetSdkVersion();
+ if (ps.getPkg() != null) {
+ int v = ps.getPkg().getTargetSdkVersion();
if (v < vers) vers = v;
}
}
return vers;
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
- if (ps.pkg != null) {
- return ps.pkg.getTargetSdkVersion();
+ if (ps.getPkg() != null) {
+ return ps.getPkg().getTargetSdkVersion();
}
}
return Build.VERSION_CODES.CUR_DEVELOPMENT;
@@ -11237,22 +10649,7 @@
}
private void postPreferredActivityChangedBroadcast(int userId) {
- mHandler.post(() -> {
- final IActivityManager am = ActivityManager.getService();
- if (am == null) {
- return;
- }
-
- final Intent intent = new Intent(Intent.ACTION_PREFERRED_ACTIVITY_CHANGED);
- intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- try {
- am.broadcastIntentWithFeature(null, null, intent, null, null,
- 0, null, null, null, null, android.app.AppOpsManager.OP_NONE,
- null, false, false, userId);
- } catch (RemoteException e) {
- }
- });
+ mHandler.post(() -> mBroadcastHelper.sendPreferredActivityChangedBroadcast(userId));
}
@Override
@@ -11840,43 +11237,16 @@
return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId());
}
- /**
- * Send a {@code PackageInstaller.ACTION_SESSION_UPDATED} broadcast intent, containing
- * the {@code sessionInfo} in the extra field {@code PackageInstaller.EXTRA_SESSION}.
- */
- public void sendSessionUpdatedBroadcast(PackageInstaller.SessionInfo sessionInfo,
- int userId) {
- if (TextUtils.isEmpty(sessionInfo.installerPackageName)) {
- return;
- }
- Intent sessionUpdatedIntent = new Intent(PackageInstaller.ACTION_SESSION_UPDATED)
- .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
- .setPackage(sessionInfo.installerPackageName);
- mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId));
- }
-
public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) {
UserManagerService ums = UserManagerService.getInstance();
- if (ums != null && !sessionInfo.isStaged()) {
- final UserInfo parent = ums.getProfileParent(userId);
- final int launcherUid = (parent != null) ? parent.id : userId;
- final ComponentName launcherComponent = getDefaultHomeActivity(launcherUid);
- if (launcherComponent != null) {
- Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
- .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
- .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
- .setPackage(launcherComponent.getPackageName());
- mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUid));
- }
- // TODO(b/122900055) Change/Remove this and replace with new permission role.
- if (mAppPredictionServicePackage != null) {
- Intent predictorIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
- .putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
- .putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
- .setPackage(mAppPredictionServicePackage);
- mContext.sendBroadcastAsUser(predictorIntent, UserHandle.of(launcherUid));
- }
+ if (ums == null || sessionInfo.isStaged()) {
+ return;
}
+ final UserInfo parent = ums.getProfileParent(userId);
+ final int launcherUid = (parent != null) ? parent.id : userId;
+ final ComponentName launcherComponent = getDefaultHomeActivity(launcherUid);
+ mBroadcastHelper.sendSessionCommitBroadcast(sessionInfo, userId, launcherUid,
+ launcherComponent, mAppPredictionServicePackage);
}
/**
@@ -12483,7 +11853,6 @@
Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
+ "commpressed package " + setting.getPackageName());
updateAllowed[i] = false;
- continue;
}
}
}
@@ -12567,7 +11936,7 @@
final String packageName = sendNowBroadcasts.keyAt(i);
final ArrayList<String> components = sendNowBroadcasts.valueAt(i);
final int packageUid = UserHandle.getUid(
- userId, pkgSettings.get(packageName).appId);
+ userId, pkgSettings.get(packageName).getAppId());
sendPackageChangedBroadcast(packageName, false /* dontKillApp */,
components, packageUid, null /* reason */);
}
@@ -12664,39 +12033,32 @@
void sendPackageChangedBroadcast(String packageName,
boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason) {
- if (DEBUG_INSTALL)
- Log.v(TAG, "Sending package changed: package=" + packageName + " components="
- + componentNames);
- Bundle extras = new Bundle(4);
- extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0));
- String[] nameList = new String[componentNames.size()];
- componentNames.toArray(nameList);
- extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
- extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, dontKillApp);
- extras.putInt(Intent.EXTRA_UID, packageUid);
- if (reason != null) {
- extras.putString(Intent.EXTRA_REASON, reason);
- }
- // If this is not reporting a change of the overall package, then only send it
- // to registered receivers. We don't want to launch a swath of apps for every
- // little component state change.
- final int flags = !componentNames.contains(packageName)
- ? Intent.FLAG_RECEIVER_REGISTERED_ONLY : 0;
final int userId = UserHandle.getUserId(packageUid);
final boolean isInstantApp = isInstantApp(packageName, userId);
final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
+ final SparseArray<int[]> broadcastAllowList = getBroadcastAllowList(
+ packageName, userIds, isInstantApp);
+ mHandler.post(() -> mBroadcastHelper.sendPackageChangedBroadcast(
+ packageName, dontKillApp, componentNames, packageUid, reason, userIds,
+ instantUserIds, broadcastAllowList));
+ }
+
+ private SparseArray<int[]> getBroadcastAllowList(String packageName, int[] userIds,
+ boolean isInstantApp) {
+ if (isInstantApp) {
+ return null;
+ }
final SparseArray<int[]> broadcastAllowList;
synchronized (mLock) {
PackageSetting setting = getPackageSettingInternal(packageName, Process.SYSTEM_UID);
if (setting == null) {
- return;
+ return null;
}
- broadcastAllowList = isInstantApp ? null : mAppsFilter.getVisibilityAllowList(setting,
- userIds, mSettings.getPackagesLocked());
+ broadcastAllowList = mAppsFilter.getVisibilityAllowList(
+ setting, userIds, mSettings.getPackagesLocked());
}
- sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, flags, null, null,
- userIds, instantUserIds, broadcastAllowList, null);
+ return broadcastAllowList;
}
@Override
@@ -12858,10 +12220,10 @@
return null;
}
- return ps.installSource;
+ return ps.getInstallSource();
}
- public boolean isOrphaned(String packageName) {
+ private boolean isOrphaned(String packageName) {
// reader
synchronized (mLock) {
if (!mPackages.containsKey(packageName)) {
@@ -13134,7 +12496,7 @@
@Override
public boolean hasSystemUidErrors() {
// allow instant applications
- return mHasSystemUidErrors;
+ return false;
}
@Override
@@ -13885,34 +13247,7 @@
}
// ------- apps on sdcard specific code -------
- static final boolean DEBUG_SD_INSTALL = false;
-
- void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
- ArrayList<String> pkgList, int[] uidArr, IIntentReceiver finishedReceiver) {
- sendResourcesChangedBroadcast(mediaStatus, replacing,
- pkgList.toArray(new String[pkgList.size()]), uidArr, finishedReceiver);
- }
-
- void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
- String[] pkgList, int[] uidArr, IIntentReceiver finishedReceiver) {
- int size = pkgList.length;
- if (size > 0) {
- // Send broadcasts here
- Bundle extras = new Bundle();
- extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST, pkgList);
- if (uidArr != null) {
- extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
- }
- if (replacing) {
- extras.putBoolean(Intent.EXTRA_REPLACING, replacing);
- }
- String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
- : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
- // TODO: not sure how to handle this one.
- sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver,
- null, null, null, null);
- }
- }
+ private static final boolean DEBUG_SD_INSTALL = false;
private void assertPackageKnownAndInstalled(String volumeUuid, String packageName, int userId)
throws PackageManagerException {
@@ -13923,10 +13258,10 @@
final PackageSetting ps = mSettings.getPackageLPr(packageName);
if (ps == null) {
throw new PackageManagerException("Package " + packageName + " is unknown");
- } else if (!TextUtils.equals(volumeUuid, ps.volumeUuid)) {
+ } else if (!TextUtils.equals(volumeUuid, ps.getVolumeUuid())) {
throw new PackageManagerException(
"Package " + packageName + " found on unknown volume " + volumeUuid
- + "; expected volume " + ps.volumeUuid);
+ + "; expected volume " + ps.getVolumeUuid());
} else if (!ps.getInstalled(userId)) {
throw new PackageManagerException(
"Package " + packageName + " not installed for user " + userId);
@@ -14039,21 +13374,21 @@
}
int preparedCount = 0;
for (PackageSetting ps : packages) {
- final String packageName = ps.name;
- if (ps.pkg == null) {
+ final String packageName = ps.getPackageName();
+ if (ps.getPkg() == null) {
Slog.w(TAG, "Odd, missing scanned package " + packageName);
// TODO: might be due to legacy ASEC apps; we should circle back
// and reconcile again once they're scanned
continue;
}
// Skip non-core apps if requested
- if (onlyCoreApps && !ps.pkg.isCoreApp()) {
+ if (onlyCoreApps && !ps.getPkg().isCoreApp()) {
result.add(packageName);
continue;
}
if (ps.getInstalled(userId)) {
- prepareAppDataAndMigrate(batch, ps.pkg, userId, flags, migrateAppData);
+ prepareAppDataAndMigrate(batch, ps.getPkg(), userId, flags, migrateAppData);
preparedCount++;
}
}
@@ -14439,13 +13774,13 @@
final int numPackages = mSettings.getPackagesLocked().size();
for (int index = 0; index < numPackages; index++) {
final PackageSetting ps = mSettings.getPackagesLocked().valueAt(index);
- if (ps.pkg == null) {
+ if (ps.getPkg() == null) {
continue;
}
- final String packageName = ps.pkg.getPackageName();
+ final String packageName = ps.getPkg().getPackageName();
// Skip over if system app or static shared library
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0
- || !TextUtils.isEmpty(ps.pkg.getStaticSharedLibName())) {
+ || !TextUtils.isEmpty(ps.getPkg().getStaticSharedLibName())) {
continue;
}
if (DEBUG_CLEAN_APKS) {
@@ -14939,7 +14274,7 @@
if (packageSetting == null) {
return false;
}
- AndroidPackage pkg = packageSetting.pkg;
+ AndroidPackage pkg = packageSetting.getPkg();
if (pkg == null) {
// May happen if package in on a removable sd card
return false;
@@ -15097,7 +14432,8 @@
String getDisabledSystemPackageName(@NonNull String packageName) {
PackageSetting disabledPkgSetting = getDisabledSystemPackage(
packageName);
- AndroidPackage disabledPkg = disabledPkgSetting == null ? null : disabledPkgSetting.pkg;
+ AndroidPackage disabledPkg = disabledPkgSetting == null
+ ? null : disabledPkgSetting.getPkg();
return disabledPkg == null ? null : disabledPkg.getPackageName();
}
@@ -15960,9 +15296,10 @@
return false;
}
final PackageSetting installerPackageSetting =
- mSettings.getPackageLPr(packageSetting.installSource.installerPackageName);
+ mSettings.getPackageLPr(packageSetting.getInstallSource()
+ .installerPackageName);
return installerPackageSetting != null
- && UserHandle.isSameApp(installerPackageSetting.appId, callingUid);
+ && UserHandle.isSameApp(installerPackageSetting.getAppId(), callingUid);
}
}
@@ -15995,7 +15332,7 @@
if (pkg == null) {
throw new IllegalStateException("No package found for " + packageName);
}
- mAppsFilter.getFeatureConfig().enableLogging(pkg.appId, enable);
+ mAppsFilter.getFeatureConfig().enableLogging(pkg.getAppId(), enable);
}
@Override
@@ -16052,7 +15389,7 @@
if (ps == null) {
return null;
}
- return ps.getIncrementalStates();
+ return ps.getIncrementalStatesInfo();
}
@Override
@@ -16185,14 +15522,14 @@
return EmptyArray.STRING;
}
- ArraySet<PackageSetting> packages = packageSetting.sharedUser.packages;
+ ArraySet<PackageSetting> packages = packageSetting.getSharedUser().packages;
final int numPackages = packages.size();
String[] res = new String[numPackages];
int i = 0;
for (int index = 0; index < numPackages; index++) {
final PackageSetting ps = packages.valueAt(index);
if (ps.getInstalled(userId)) {
- res[i++] = ps.name;
+ res[i++] = ps.getPackageName();
}
}
res = ArrayUtils.trimToSize(res, i);
@@ -16208,7 +15545,7 @@
return PackageInfoUtils.generateProcessInfo(sus.processes, 0);
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
- return PackageInfoUtils.generateProcessInfo(ps.pkg.getProcesses(), 0);
+ return PackageInfoUtils.generateProcessInfo(ps.getPkg().getProcesses(), 0);
}
return null;
}
@@ -16515,7 +15852,7 @@
PackageDexUsage.PackageUseInfo packageUseInfo =
getDexManager().getPackageUseInfoOrDefault(pkg.getPackageName());
if (PackageManagerServiceUtils
- .isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
+ .isUnusedSinceTimeInMillis(ps.getFirstInstallTime(), currentTimeInMillis,
downgradeTimeThresholdMillis, packageUseInfo,
ps.getPkgState().getLatestPackageUseTimeInMills(),
ps.getPkgState().getLatestForegroundPackageUseTimeInMills())) {
@@ -16591,19 +15928,7 @@
final long ident = Binder.clearCallingIdentity();
try {
- final Intent intent = new Intent(Intent.ACTION_DEVICE_CUSTOMIZATION_READY);
- intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- final IActivityManager am = ActivityManager.getService();
- final String[] requiredPermissions = {
- Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY,
- };
- try {
- am.broadcastIntentWithFeature(null, null, intent, null, null, 0, null, null,
- requiredPermissions, null, android.app.AppOpsManager.OP_NONE, null, false,
- false, UserHandle.USER_ALL);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ BroadcastHelper.sendDeviceCustomizationReadyBroadcast();
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -16766,9 +16091,10 @@
}
continue;
}
- if (ps.appId < Process.FIRST_APPLICATION_UID) {
+ if (ps.getAppId() < Process.FIRST_APPLICATION_UID) {
if (DEBUG_PER_UID_READ_TIMEOUTS) {
- Slog.i(TAG, "PerUidReadTimeouts: package is system, appId=" + ps.appId);
+ Slog.i(TAG, "PerUidReadTimeouts: package is system, appId="
+ + ps.getAppId());
}
continue;
}
@@ -16795,7 +16121,7 @@
if (!ps.getInstalled(userId)) {
continue;
}
- final int uid = UserHandle.getUid(userId, ps.appId);
+ final int uid = UserHandle.getUid(userId, ps.getAppId());
final PerUidReadTimeouts perUid = new PerUidReadTimeouts();
perUid.uid = uid;
perUid.minTimeUs = perPackage.timeouts.minTimeUs;
@@ -16808,21 +16134,6 @@
return result.toArray(new PerUidReadTimeouts[result.size()]);
}
- static @NonNull BroadcastOptions getTemporaryAppAllowlistBroadcastOptions(
- @PowerWhitelistManager.ReasonCode int reasonCode) {
- long duration = 10_000;
- final ActivityManagerInternal amInternal =
- LocalServices.getService(ActivityManagerInternal.class);
- if (amInternal != null) {
- duration = amInternal.getBootTimeTempAllowListDuration();
- }
- final BroadcastOptions bOptions = BroadcastOptions.makeBasic();
- bOptions.setTemporaryAppAllowlist(duration,
- TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
- reasonCode, "");
- return bOptions;
- }
-
@Override
public void setKeepUninstalledPackages(List<String> packageList) {
mContext.enforceCallingPermission(
@@ -16936,7 +16247,7 @@
+ (filterTarget ? targetPackageName + " " : "")
+ "not found."));
}
- final int sourcePackageUid = UserHandle.getUid(userId, sourceSetting.appId);
+ final int sourcePackageUid = UserHandle.getUid(userId, sourceSetting.getAppId());
return !shouldFilterApplicationLocked(targetSetting, sourcePackageUid, userId);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
new file mode 100644
index 0000000..a63cc36
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceInjector.java
@@ -0,0 +1,385 @@
+/*
+ * 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.pm;
+
+import android.app.ActivityManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.incremental.IncrementalManager;
+import android.util.DisplayMetrics;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemConfig;
+import com.android.server.compat.PlatformCompat;
+import com.android.server.pm.dex.ArtManagerService;
+import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.ViewCompiler;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal;
+import com.android.server.pm.permission.PermissionManagerServiceInternal;
+import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
+ *
+ * NOTE: All getters should return the same instance for every call.
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+public class PackageManagerServiceInjector {
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ interface Producer<T> {
+ /** Produce an instance of type {@link T} */
+ T produce(PackageManagerServiceInjector injector, PackageManagerService packageManager);
+ }
+
+ interface ProducerWithArgument<T, R> {
+ T produce(PackageManagerServiceInjector injector, PackageManagerService packageManager,
+ R argument);
+ }
+
+ interface ServiceProducer {
+ <T> T produce(Class<T> c);
+ }
+
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+ static class Singleton<T> {
+ private final Producer<T> mProducer;
+ private volatile T mInstance = null;
+
+ Singleton(Producer<T> producer) {
+ this.mProducer = producer;
+ }
+
+ T get(PackageManagerServiceInjector injector,
+ PackageManagerService packageManagerService) {
+ if (mInstance == null) {
+ mInstance = mProducer.produce(injector, packageManagerService);
+ }
+ return mInstance;
+ }
+ }
+
+ private PackageManagerService mPackageManager;
+
+ private final PackageAbiHelper mAbiHelper;
+ private final Context mContext;
+ private final PackageManagerTracedLock mLock;
+ private final Installer mInstaller;
+ private final Object mInstallLock;
+ private final Handler mBackgroundHandler;
+ private final Executor mBackgroundExecutor;
+ private final List<ScanPartition> mSystemPartitions;
+
+ // ----- producers -----
+ private final Singleton<ComponentResolver>
+ mComponentResolverProducer;
+ private final Singleton<PermissionManagerServiceInternal>
+ mPermissionManagerServiceProducer;
+ private final Singleton<UserManagerService>
+ mUserManagerProducer;
+ private final Singleton<Settings> mSettingsProducer;
+ private final Singleton<AppsFilter> mAppsFilterProducer;
+ private final Singleton<PlatformCompat>
+ mPlatformCompatProducer;
+ private final Singleton<SystemConfig> mSystemConfigProducer;
+ private final Singleton<PackageDexOptimizer>
+ mPackageDexOptimizerProducer;
+ private final Singleton<DexManager> mDexManagerProducer;
+ private final Singleton<ArtManagerService>
+ mArtManagerServiceProducer;
+ private final Singleton<ApexManager> mApexManagerProducer;
+ private final Singleton<ViewCompiler> mViewCompilerProducer;
+ private final Singleton<IncrementalManager>
+ mIncrementalManagerProducer;
+ private final Singleton<DefaultAppProvider>
+ mDefaultAppProviderProducer;
+ private final Singleton<DisplayMetrics>
+ mDisplayMetricsProducer;
+ private final Producer<PackageParser2>
+ mScanningCachingPackageParserProducer;
+ private final Producer<PackageParser2>
+ mScanningPackageParserProducer;
+ private final Producer<PackageParser2>
+ mPreparingPackageParserProducer;
+ private final Singleton<PackageInstallerService>
+ mPackageInstallerServiceProducer;
+ private final ProducerWithArgument<InstantAppResolverConnection, ComponentName>
+ mInstantAppResolverConnectionProducer;
+ private final Singleton<LegacyPermissionManagerInternal>
+ mLegacyPermissionManagerInternalProducer;
+ private final SystemWrapper mSystemWrapper;
+ private final ServiceProducer mGetLocalServiceProducer;
+ private final ServiceProducer mGetSystemServiceProducer;
+ private final Singleton<ModuleInfoProvider>
+ mModuleInfoProviderProducer;
+ private final Singleton<DomainVerificationManagerInternal>
+ mDomainVerificationManagerInternalProducer;
+ private final Singleton<Handler> mHandlerProducer;
+
+ PackageManagerServiceInjector(Context context, PackageManagerTracedLock lock,
+ Installer installer, Object installLock, PackageAbiHelper abiHelper,
+ Handler backgroundHandler,
+ List<ScanPartition> systemPartitions,
+ Producer<ComponentResolver> componentResolverProducer,
+ Producer<PermissionManagerServiceInternal> permissionManagerServiceProducer,
+ Producer<UserManagerService> userManagerProducer,
+ Producer<Settings> settingsProducer,
+ Producer<AppsFilter> appsFilterProducer,
+ Producer<PlatformCompat> platformCompatProducer,
+ Producer<SystemConfig> systemConfigProducer,
+ Producer<PackageDexOptimizer> packageDexOptimizerProducer,
+ Producer<DexManager> dexManagerProducer,
+ Producer<ArtManagerService> artManagerServiceProducer,
+ Producer<ApexManager> apexManagerProducer,
+ Producer<ViewCompiler> viewCompilerProducer,
+ Producer<IncrementalManager> incrementalManagerProducer,
+ Producer<DefaultAppProvider> defaultAppProviderProducer,
+ Producer<DisplayMetrics> displayMetricsProducer,
+ Producer<PackageParser2> scanningCachingPackageParserProducer,
+ Producer<PackageParser2> scanningPackageParserProducer,
+ Producer<PackageParser2> preparingPackageParserProducer,
+ Producer<PackageInstallerService> packageInstallerServiceProducer,
+ ProducerWithArgument<InstantAppResolverConnection,
+ ComponentName>
+ instantAppResolverConnectionProducer,
+ Producer<ModuleInfoProvider> moduleInfoProviderProducer,
+ Producer<LegacyPermissionManagerInternal> legacyPermissionManagerInternalProducer,
+ Producer<DomainVerificationManagerInternal>
+ domainVerificationManagerInternalProducer,
+ Producer<Handler> handlerProducer,
+ SystemWrapper systemWrapper,
+ ServiceProducer getLocalServiceProducer,
+ ServiceProducer getSystemServiceProducer) {
+ mContext = context;
+ mLock = lock;
+ mInstaller = installer;
+ mAbiHelper = abiHelper;
+ mInstallLock = installLock;
+ mBackgroundHandler = backgroundHandler;
+ mBackgroundExecutor = new HandlerExecutor(backgroundHandler);
+ mSystemPartitions = systemPartitions;
+ mComponentResolverProducer = new Singleton<>(
+ componentResolverProducer);
+ mPermissionManagerServiceProducer = new Singleton<>(
+ permissionManagerServiceProducer);
+ mUserManagerProducer = new Singleton<>(userManagerProducer);
+ mSettingsProducer = new Singleton<>(settingsProducer);
+ mAppsFilterProducer = new Singleton<>(appsFilterProducer);
+ mPlatformCompatProducer = new Singleton<>(
+ platformCompatProducer);
+ mSystemConfigProducer = new Singleton<>(systemConfigProducer);
+ mPackageDexOptimizerProducer = new Singleton<>(
+ packageDexOptimizerProducer);
+ mDexManagerProducer = new Singleton<>(dexManagerProducer);
+ mArtManagerServiceProducer = new Singleton<>(
+ artManagerServiceProducer);
+ mApexManagerProducer = new Singleton<>(apexManagerProducer);
+ mViewCompilerProducer = new Singleton<>(viewCompilerProducer);
+ mIncrementalManagerProducer = new Singleton<>(
+ incrementalManagerProducer);
+ mDefaultAppProviderProducer = new Singleton<>(
+ defaultAppProviderProducer);
+ mDisplayMetricsProducer = new Singleton<>(
+ displayMetricsProducer);
+ mScanningCachingPackageParserProducer = scanningCachingPackageParserProducer;
+ mScanningPackageParserProducer = scanningPackageParserProducer;
+ mPreparingPackageParserProducer = preparingPackageParserProducer;
+ mPackageInstallerServiceProducer = new Singleton<>(
+ packageInstallerServiceProducer);
+ mInstantAppResolverConnectionProducer = instantAppResolverConnectionProducer;
+ mModuleInfoProviderProducer = new Singleton<>(
+ moduleInfoProviderProducer);
+ mLegacyPermissionManagerInternalProducer = new Singleton<>(
+ legacyPermissionManagerInternalProducer);
+ mSystemWrapper = systemWrapper;
+ mGetLocalServiceProducer = getLocalServiceProducer;
+ mGetSystemServiceProducer = getSystemServiceProducer;
+ mDomainVerificationManagerInternalProducer =
+ new Singleton<>(
+ domainVerificationManagerInternalProducer);
+ mHandlerProducer = new Singleton<>(handlerProducer);
+ }
+
+ /**
+ * Bootstraps this injector with the {@link PackageManagerService instance to which it
+ * belongs.
+ */
+ public void bootstrap(PackageManagerService pm) {
+ this.mPackageManager = pm;
+ }
+
+ public UserManagerInternal getUserManagerInternal() {
+ return getUserManagerService().getInternalForInjectorOnly();
+ }
+
+ public PackageAbiHelper getAbiHelper() {
+ return mAbiHelper;
+ }
+
+ public Object getInstallLock() {
+ return mInstallLock;
+ }
+
+ public List<ScanPartition> getSystemPartitions() {
+ return mSystemPartitions;
+ }
+
+ public UserManagerService getUserManagerService() {
+ return mUserManagerProducer.get(this, mPackageManager);
+ }
+
+ public PackageManagerTracedLock getLock() {
+ return mLock;
+ }
+
+ public Installer getInstaller() {
+ return mInstaller;
+ }
+
+ public ComponentResolver getComponentResolver() {
+ return mComponentResolverProducer.get(this, mPackageManager);
+ }
+
+ public PermissionManagerServiceInternal getPermissionManagerServiceInternal() {
+ return mPermissionManagerServiceProducer.get(this, mPackageManager);
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public Settings getSettings() {
+ return mSettingsProducer.get(this, mPackageManager);
+ }
+
+ public AppsFilter getAppsFilter() {
+ return mAppsFilterProducer.get(this, mPackageManager);
+ }
+
+ public PlatformCompat getCompatibility() {
+ return mPlatformCompatProducer.get(this, mPackageManager);
+ }
+
+ public SystemConfig getSystemConfig() {
+ return mSystemConfigProducer.get(this, mPackageManager);
+ }
+
+ public PackageDexOptimizer getPackageDexOptimizer() {
+ return mPackageDexOptimizerProducer.get(this, mPackageManager);
+ }
+
+ public DexManager getDexManager() {
+ return mDexManagerProducer.get(this, mPackageManager);
+ }
+
+ public ArtManagerService getArtManagerService() {
+ return mArtManagerServiceProducer.get(this, mPackageManager);
+ }
+
+ public ApexManager getApexManager() {
+ return mApexManagerProducer.get(this, mPackageManager);
+ }
+
+ public ViewCompiler getViewCompiler() {
+ return mViewCompilerProducer.get(this, mPackageManager);
+ }
+
+ public Handler getBackgroundHandler() {
+ return mBackgroundHandler;
+ }
+
+ public Executor getBackgroundExecutor() {
+ return mBackgroundExecutor;
+ }
+
+ public DisplayMetrics getDisplayMetrics() {
+ return mDisplayMetricsProducer.get(this, mPackageManager);
+ }
+
+ public <T> T getLocalService(Class<T> c) {
+ return mGetLocalServiceProducer.produce(c);
+ }
+
+ public <T> T getSystemService(Class<T> c) {
+ return mGetSystemServiceProducer.produce(c);
+ }
+
+ public SystemWrapper getSystemWrapper() {
+ return mSystemWrapper;
+ }
+
+ public IncrementalManager getIncrementalManager() {
+ return mIncrementalManagerProducer.get(this, mPackageManager);
+ }
+
+ public DefaultAppProvider getDefaultAppProvider() {
+ return mDefaultAppProviderProducer.get(this, mPackageManager);
+ }
+
+ public PackageParser2 getScanningCachingPackageParser() {
+ return mScanningCachingPackageParserProducer.produce(this, mPackageManager);
+ }
+
+ public PackageParser2 getScanningPackageParser() {
+ return mScanningPackageParserProducer.produce(this, mPackageManager);
+ }
+
+ public PackageParser2 getPreparingPackageParser() {
+ return mPreparingPackageParserProducer.produce(this, mPackageManager);
+ }
+
+ public PackageInstallerService getPackageInstallerService() {
+ return mPackageInstallerServiceProducer.get(this, mPackageManager);
+ }
+
+ public InstantAppResolverConnection getInstantAppResolverConnection(
+ ComponentName instantAppResolverComponent) {
+ return mInstantAppResolverConnectionProducer.produce(
+ this, mPackageManager, instantAppResolverComponent);
+ }
+
+ public ModuleInfoProvider getModuleInfoProvider() {
+ return mModuleInfoProviderProducer.get(this, mPackageManager);
+ }
+
+ public LegacyPermissionManagerInternal getLegacyPermissionManagerInternal() {
+ return mLegacyPermissionManagerInternalProducer.get(this, mPackageManager);
+ }
+
+ public DomainVerificationManagerInternal getDomainVerificationManagerInternal() {
+ return mDomainVerificationManagerInternalProducer.get(this, mPackageManager);
+ }
+
+ public Handler getHandler() {
+ return mHandlerProducer.get(this, mPackageManager);
+ }
+
+ public ActivityManagerInternal getActivityManagerInternal() {
+ return getLocalService(ActivityManagerInternal.class);
+ }
+
+ /** Provides an abstraction to static access to system state. */
+ public interface SystemWrapper {
+ void disablePackageCaches();
+ void enablePackageCaches();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
new file mode 100644
index 0000000..2870c45
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -0,0 +1,104 @@
+/*
+ * 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.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.pm.FeatureInfo;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.TestUtilityService;
+import android.os.Build;
+import android.os.Handler;
+import android.os.incremental.IncrementalManager;
+import android.util.ArrayMap;
+import android.util.DisplayMetrics;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.content.om.OverlayConfig;
+import com.android.server.pm.dex.ArtManagerService;
+import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.ViewCompiler;
+import com.android.server.pm.parsing.PackageParser2;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal;
+
+import java.io.File;
+import java.util.List;
+
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+public final class PackageManagerServiceTestParams {
+ public ApexManager apexManager;
+ public @Nullable String appPredictionServicePackage;
+ public ArtManagerService artManagerService;
+ public @Nullable String configuratorPackage;
+ public int defParseFlags;
+ public DefaultAppProvider defaultAppProvider;
+ public DexManager dexManager;
+ public List<ScanPartition> dirsToScanAsSystem;
+ public boolean factoryTest;
+ public ArrayMap<String, FeatureInfo> availableFeatures;
+ public Handler handler;
+ public @Nullable String incidentReportApproverPackage;
+ public IncrementalManager incrementalManager;
+ public PackageInstallerService installerService;
+ public InstantAppRegistry instantAppRegistry;
+ public InstantAppResolverConnection instantAppResolverConnection;
+ public ComponentName instantAppResolverSettingsComponent;
+ public boolean isPreNmr1Upgrade;
+ public boolean isPreNupgrade;
+ public boolean isPreQupgrade;
+ public boolean isUpgrade;
+ public LegacyPermissionManagerInternal legacyPermissionManagerInternal;
+ public DisplayMetrics Metrics;
+ public ModuleInfoProvider moduleInfoProvider;
+ public MovePackageHelper.MoveCallbacks moveCallbacks;
+ public boolean onlyCore;
+ public OverlayConfig overlayConfig;
+ public PackageDexOptimizer packageDexOptimizer;
+ public PackageParser2.Callback packageParserCallback;
+ public PendingPackageBroadcasts pendingPackageBroadcasts;
+ public PackageManagerInternal pmInternal;
+ public TestUtilityService testUtilityService;
+ public ProcessLoggingHandler processLoggingHandler;
+ public ProtectedPackages protectedPackages;
+ public @NonNull String requiredInstallerPackage;
+ public @NonNull String requiredPermissionControllerPackage;
+ public @NonNull String requiredUninstallerPackage;
+ public @Nullable String requiredVerifierPackage;
+ public String[] separateProcesses;
+ public @NonNull String servicesExtensionPackageName;
+ public @Nullable String setupWizardPackage;
+ public @NonNull String sharedSystemSharedLibraryPackageName;
+ public @Nullable String storageManagerPackage;
+ public @Nullable String defaultTextClassifierPackage;
+ public @Nullable String systemTextClassifierPackage;
+ public @Nullable String overlayConfigSignaturePackage;
+ public ViewCompiler viewCompiler;
+ public @Nullable String retailDemoPackage;
+ public @Nullable String recentsPackage;
+ public ComponentName resolveComponentName;
+ public ArrayMap<String, AndroidPackage> packages;
+ public boolean enableFreeCacheV2;
+ public int sdkVersion;
+ public File appInstallDir;
+ public File appLib32InstallDir;
+ public boolean isEngBuild;
+ public boolean isUserDebugBuild;
+ public int sdkInt = Build.VERSION.SDK_INT;
+ public final String incrementalVersion = Build.VERSION.INCREMENTAL;
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 709fc93..144b2b0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -134,7 +134,7 @@
private static final long MAX_CRITICAL_INFO_DUMP_SIZE = 3 * 1000 * 1000; // 3MB
public final static Predicate<PackageSetting> REMOVE_IF_NULL_PKG =
- pkgSetting -> pkgSetting.pkg == null;
+ pkgSetting -> pkgSetting.getPkg() == null;
/**
* Components of apps targeting Android T and above will stop receiving intents from
@@ -237,19 +237,19 @@
ArrayList<PackageSetting> sortTemp = new ArrayList<>(remainingPkgSettings.size());
// Give priority to core apps.
- applyPackageFilter(pkgSetting -> pkgSetting.pkg.isCoreApp(), result, remainingPkgSettings, sortTemp,
- packageManagerService);
+ applyPackageFilter(pkgSetting -> pkgSetting.getPkg().isCoreApp(), result,
+ remainingPkgSettings, sortTemp, packageManagerService);
// Give priority to system apps that listen for pre boot complete.
Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
final ArraySet<String> pkgNames = getPackageNamesForIntent(intent, UserHandle.USER_SYSTEM);
- applyPackageFilter(pkgSetting -> pkgNames.contains(pkgSetting.name), result,
+ applyPackageFilter(pkgSetting -> pkgNames.contains(pkgSetting.getPackageName()), result,
remainingPkgSettings, sortTemp, packageManagerService);
// Give priority to apps used by other apps.
DexManager dexManager = packageManagerService.getDexManager();
applyPackageFilter(pkgSetting ->
- dexManager.getPackageUseInfoOrDefault(pkgSetting.name)
+ dexManager.getPackageUseInfoOrDefault(pkgSetting.getPackageName())
.isAnyCodePathUsedByOtherApps(),
result, remainingPkgSettings, sortTemp, packageManagerService);
@@ -266,7 +266,7 @@
pkgSetting1.getPkgState().getLatestForegroundPackageUseTimeInMills(),
pkgSetting2.getPkgState().getLatestForegroundPackageUseTimeInMills()));
if (debug) {
- Log.i(TAG, "Taking package " + lastUsed.name
+ Log.i(TAG, "Taking package " + lastUsed.getPackageName()
+ " as reference in time use");
}
long estimatedPreviousSystemUseTime = lastUsed.getPkgState()
@@ -349,7 +349,7 @@
if (sb.length() > 0) {
sb.append(", ");
}
- sb.append(pkgSettings.get(index).name);
+ sb.append(pkgSettings.get(index).getPackageName());
}
return sb.toString();
}
@@ -538,7 +538,7 @@
*/
public static boolean comparePackageSignatures(PackageSetting pkgSetting,
Signature[] signatures) {
- final SigningDetails signingDetails = pkgSetting.signatures.mSigningDetails;
+ final SigningDetails signingDetails = pkgSetting.getSigningDetails();
return signingDetails == SigningDetails.UNKNOWN
|| compareSignatures(signingDetails.getSignatures(), signatures)
== PackageManager.SIGNATURE_MATCH;
@@ -613,16 +613,16 @@
*/
private static boolean matchSignatureInSystem(PackageSetting pkgSetting,
PackageSetting disabledPkgSetting) {
- if (pkgSetting.signatures.mSigningDetails.checkCapability(
- disabledPkgSetting.signatures.mSigningDetails,
+ if (pkgSetting.getSigningDetails().checkCapability(
+ disabledPkgSetting.getSigningDetails(),
SigningDetails.CertCapabilities.INSTALLED_DATA)
- || disabledPkgSetting.signatures.mSigningDetails.checkCapability(
- pkgSetting.signatures.mSigningDetails,
+ || disabledPkgSetting.getSigningDetails().checkCapability(
+ pkgSetting.getSigningDetails(),
SigningDetails.CertCapabilities.ROLLBACK)) {
return true;
} else {
logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
- pkgSetting.name);
+ pkgSetting.getPackageName());
return false;
}
}
@@ -665,31 +665,31 @@
PackageSetting disabledPkgSetting, SigningDetails parsedSignatures,
boolean compareCompat, boolean compareRecover, boolean isRollback)
throws PackageManagerException {
- final String packageName = pkgSetting.name;
+ final String packageName = pkgSetting.getPackageName();
boolean compatMatch = false;
- if (pkgSetting.signatures.mSigningDetails.getSignatures() != null) {
+ if (pkgSetting.getSigningDetails().getSignatures() != null) {
// Already existing package. Make sure signatures match
boolean match = parsedSignatures.checkCapability(
- pkgSetting.signatures.mSigningDetails,
+ pkgSetting.getSigningDetails(),
SigningDetails.CertCapabilities.INSTALLED_DATA)
- || pkgSetting.signatures.mSigningDetails.checkCapability(
+ || pkgSetting.getSigningDetails().checkCapability(
parsedSignatures,
SigningDetails.CertCapabilities.ROLLBACK);
if (!match && compareCompat) {
- match = matchSignaturesCompat(packageName, pkgSetting.signatures,
+ match = matchSignaturesCompat(packageName, pkgSetting.getSignatures(),
parsedSignatures);
compatMatch = match;
}
if (!match && compareRecover) {
match = matchSignaturesRecover(
packageName,
- pkgSetting.signatures.mSigningDetails,
+ pkgSetting.getSigningDetails(),
parsedSignatures,
SigningDetails.CertCapabilities.INSTALLED_DATA)
|| matchSignaturesRecover(
packageName,
parsedSignatures,
- pkgSetting.signatures.mSigningDetails,
+ pkgSetting.getSigningDetails(),
SigningDetails.CertCapabilities.ROLLBACK);
}
@@ -701,7 +701,7 @@
// Since a rollback can only be initiated for an APK previously installed on the
// device allow rolling back to a previous signing key even if the rollback
// capability has not been granted.
- match = pkgSetting.signatures.mSigningDetails.hasAncestorOrSelf(parsedSignatures);
+ match = pkgSetting.getSigningDetails().hasAncestorOrSelf(parsedSignatures);
}
if (!match) {
@@ -731,7 +731,8 @@
// being the only package in the sharedUserId so far and the lineage being updated to
// deny the sharedUserId capability of the previous key in the lineage.
if (!match && pkgSetting.getSharedUser().packages.size() == 1
- && pkgSetting.getSharedUser().packages.valueAt(0).name.equals(packageName)) {
+ && pkgSetting.getSharedUser().packages
+ .valueAt(0).getPackageName().equals(packageName)) {
match = true;
}
if (!match && compareCompat) {
@@ -765,7 +766,7 @@
// if the current package in the sharedUserId is the package being updated then
// skip this check as the update may revoke the sharedUserId capability from
// the key with which this app was previously signed.
- if (packageName.equals(shUidPkgSetting.name)) {
+ if (packageName.equals(shUidPkgSetting.getPackageName())) {
continue;
}
SigningDetails shUidSigningDetails =
@@ -778,8 +779,9 @@
throw new PackageManagerException(
INSTALL_FAILED_SHARED_USER_INCOMPATIBLE,
"Package " + packageName
- + " revoked the sharedUserId capability from the "
- + "signing key used to sign " + shUidPkgSetting.name);
+ + " revoked the sharedUserId capability from the"
+ + " signing key used to sign "
+ + shUidPkgSetting.getPackageName());
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index e3581db..74e1050 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -32,7 +32,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
-final class PackageRemovedInfo {
+public final class PackageRemovedInfo {
final PackageSender mPackageSender;
String mRemovedPackage;
String mInstallerPackageName;
@@ -158,7 +158,7 @@
}
}
- void populateUsers(int[] userIds, PackageSetting deletedPackageSetting) {
+ public void populateUsers(int[] userIds, PackageSetting deletedPackageSetting) {
mRemovedUsers = userIds;
if (mRemovedUsers == null) {
mBroadcastUsers = null;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 249423e..7d99ba1 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -16,76 +16,66 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
+import android.content.pm.IncrementalStatesInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageUserState;
import android.content.pm.SharedLibraryInfo;
+import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
+import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
-import android.content.pm.pkg.PackageUserState;
+import android.content.pm.overlay.OverlayPaths;
+import android.os.PersistableBundle;
+import android.os.incremental.IncrementalManager;
import android.service.pm.PackageProto;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.CollectionUtils;
+import com.android.internal.util.DataClass;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.pkg.AndroidPackageApi;
+import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateUnserialized;
import com.android.server.utils.SnapshotCache;
+import libcore.util.EmptyArray;
+
import java.io.File;
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;
+import java.util.function.Predicate;
import java.util.UUID;
/**
* Settings data for a particular package we know about.
+ * @hide
*/
-public class PackageSetting extends PackageSettingBase {
+@DataClass(genGetters = true, genConstructor = false, genSetters = false, genBuilder = false)
+@DataClass.Suppress({"getSnapshot", })
+public class PackageSetting extends SettingBase implements PackageState {
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public int appId;
-
- /**
- * This can be null whenever a physical APK on device is missing. This can be the result of
- * removing an external storage device where the APK resides.
- *
- * This will result in the system reading the {@link PackageSetting} from disk, but without
- * being able to parse the base APK's AndroidManifest.xml to read all of its metadata. The data
- * that is written and read in {@link Settings} includes a minimal set of metadata needed to
- * perform other checks in the system.
- *
- * This is important in order to enforce uniqueness within the system, as the package, even if
- * on a removed storage device, is still considered installed. Another package of the same
- * application ID or declaring the same permissions or similar cannot be installed.
- *
- * Re-attaching the storage device to make the APK available should allow the user to use the
- * app once the device reboots or otherwise re-scans it.
- *
- * It is expected that all code that uses a {@link PackageSetting} understands this inner field
- * may be null. Note that this relationship only works one way. It should not be possible to
- * have an entry inside {@link PackageManagerService#mPackages} without a corresponding
- * {@link PackageSetting} inside {@link Settings#mPackages}.
- *
- * @deprecated Use {@link #getPkg()}. The setter is favored to avoid unintended mutation.
- */
- @Nullable
- @Deprecated
- public AndroidPackage pkg;
-
- /**
- * WARNING. The object reference is important. We perform integer equality and NOT
- * object equality to check whether shared user settings are the same.
- */
- SharedUserSetting sharedUser;
+ static final PackageUserState DEFAULT_USER_STATE = new PackageUserState();
/**
* Temporary holding space for the shared user ID. While parsing package settings, the
@@ -93,16 +83,124 @@
* shared user setting with the package setting. The shared user ID lets us link the
* two objects.
*/
- private int sharedUserId;
+ protected int sharedUserId;
/**
- * Maps mime group name to the set of Mime types in a group. Mime groups declared
- * by app are populated with empty sets at construction.
- * Mime groups can not be created/removed at runtime, thus keys in this map should not change
+ * @see PackageState#getMimeGroups()
*/
@Nullable
Map<String, ArraySet<String>> mimeGroups;
+ /**
+ * Non-persisted value. During an "upgrade without restart", we need the set
+ * of all previous code paths so we can surgically add the new APKs to the
+ * active classloader. If at any point an application is upgraded with a
+ * restart, this field will be cleared since the classloader would be created
+ * using the full set of code paths when the package's process is started.
+ * TODO: Remove
+ */
+ @Deprecated
+ @Nullable
+ Set<String> mOldCodePaths;
+
+ @NonNull
+ private IncrementalStates incrementalStates;
+
+ @Nullable
+ String[] usesStaticLibraries;
+
+ @Nullable
+ long[] usesStaticLibrariesVersions;
+
+ /**
+ * The path under which native libraries have been unpacked. This path is
+ * always derived at runtime, and is only stored here for cleanup when a
+ * package is uninstalled.
+ */
+ @Nullable
+ @Deprecated
+ private String legacyNativeLibraryPath;
+
+ @NonNull
+ private String mName;
+
+ @NonNull
+ private String mRealName;
+
+ private int mAppId;
+
+ /**
+ * It is expected that all code that uses a {@link PackageSetting} understands this inner field
+ * may be null. Note that this relationship only works one way. It should not be possible to
+ * have an entry inside {@link PackageManagerService#mPackages} without a corresponding
+ * {@link PackageSetting} inside {@link Settings#mPackages}.
+ *
+ * @see PackageState#getAndroidPackage()
+ */
+ @Nullable
+ private AndroidPackage pkg;
+
+ /**
+ * WARNING. The object reference is important. We perform integer equality and NOT
+ * object equality to check whether shared user settings are the same.
+ */
+ @Nullable
+ private SharedUserSetting sharedUser;
+
+ /** @see AndroidPackage#getPath() */
+ @NonNull
+ private File mPath;
+ @NonNull
+ private String mPathString;
+
+ @Nullable
+ private String mPrimaryCpuAbi;
+
+ @Nullable
+ private String mSecondaryCpuAbi;
+
+ /**
+ * The install time CPU override, if any. This value is written at install time
+ * and doesn't change during the life of an install. If non-null,
+ * {@code primaryCpuAbiString} will contain the same value.
+ */
+ @Nullable
+ private String mCpuAbiOverride;
+
+ private long mLastModifiedTime;
+ private long firstInstallTime;
+ private long lastUpdateTime;
+ private long versionCode;
+
+ @NonNull
+ private PackageSignatures signatures;
+
+ private boolean installPermissionsFixed;
+
+ @NonNull
+ private PackageKeySetData keySetData = new PackageKeySetData();
+
+ // TODO: Access is not locked.
+ // Whether this package is currently stopped, thus can not be
+ // started until explicitly launched by the user.
+ @NonNull
+ private final SparseArray<PackageUserState> mUserState = new SparseArray<>();
+
+ @NonNull
+ private InstallSource installSource;
+
+ /** @see PackageState#getVolumeUuid() */
+ @Nullable
+ private String volumeUuid;
+
+ /** @see PackageState#getCategoryOverride() */
+ private int categoryOverride = ApplicationInfo.CATEGORY_UNDEFINED;
+
+ /** @see PackageState#isUpdateAvailable() */
+ private boolean updateAvailable;
+
+ private boolean forceQueryableOverride;
+
@NonNull
private PackageStateUnserialized pkgState = new PackageStateUnserialized();
@@ -112,6 +210,7 @@
/**
* Snapshot support.
*/
+ @NonNull
private final SnapshotCache<PackageSetting> mSnapshot;
private SnapshotCache<PackageSetting> makeCache() {
@@ -123,17 +222,28 @@
}
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public PackageSetting(String name, String realName, @NonNull File codePath,
- String legacyNativeLibraryPathString, String primaryCpuAbiString,
- String secondaryCpuAbiString, String cpuAbiOverrideString,
- long pVersionCode, int pkgFlags, int privateFlags,
+ public PackageSetting(String name, String realName, @NonNull File path,
+ String legacyNativeLibraryPath, String primaryCpuAbi,
+ String secondaryCpuAbi, String cpuAbiOverride,
+ long longVersionCode, int pkgFlags, int pkgPrivateFlags,
int sharedUserId, String[] usesStaticLibraries,
long[] usesStaticLibrariesVersions, Map<String, ArraySet<String>> mimeGroups,
@NonNull UUID domainSetId) {
- super(name, realName, codePath, legacyNativeLibraryPathString,
- primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
- pVersionCode, pkgFlags, privateFlags,
- usesStaticLibraries, usesStaticLibrariesVersions);
+ super(pkgFlags, pkgPrivateFlags);
+ this.mName = name;
+ this.mRealName = realName;
+ this.usesStaticLibraries = usesStaticLibraries;
+ this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
+ this.mPath = path;
+ this.mPathString = path.toString();
+ this.legacyNativeLibraryPath = legacyNativeLibraryPath;
+ this.mPrimaryCpuAbi = primaryCpuAbi;
+ this.mSecondaryCpuAbi = secondaryCpuAbi;
+ this.mCpuAbiOverride = cpuAbiOverride;
+ this.versionCode = longVersionCode;
+ this.signatures = new PackageSignatures();
+ this.installSource = InstallSource.EMPTY;
+ this.incrementalStates = new IncrementalStates();
this.sharedUserId = sharedUserId;
mDomainSetId = domainSetId;
copyMimeGroups(mimeGroups);
@@ -145,39 +255,30 @@
* Note that it keeps the same PackageParser.Package instance.
*/
PackageSetting(PackageSetting orig) {
- super(orig, orig.realName);
- doCopy(orig);
- mSnapshot = makeCache();
+ this(orig, false);
}
/**
- * New instance of PackageSetting replicating the original settings, but, allows specifying
- * a real package name.
- * Note that it keeps the same PackageParser.Package instance.
+ * New instance of PackageSetting with one-level-deep cloning.
+ * <p>
+ * IMPORTANT: With a shallow copy, we do NOT create new contained objects.
+ * This means, for example, changes to the user state of the original PackageSetting
+ * will also change the user state in its copy.
*/
- PackageSetting(PackageSetting orig, String realPkgName) {
- super(orig, realPkgName);
- doCopy(orig);
- mSnapshot = makeCache();
+ PackageSetting(PackageSetting base, String realPkgName) {
+ this(base, false);
+ this.mRealName = realPkgName;
}
- /**
- * Create a snapshot. The copy constructor is already in use and cannot be modified
- * for this purpose.
- */
- PackageSetting(PackageSetting orig, boolean snapshot) {
- super(orig, snapshot);
- // The existing doCopy() method cannot be used in here because sharedUser must be
- // a snapshot, and not a reference. Also, the pkgState must be copied. However,
- // this code should otherwise be kept in sync with doCopy().
- appId = orig.appId;
- pkg = orig.pkg;
- sharedUser = orig.sharedUser == null ? null : orig.sharedUser.snapshot();
- sharedUserId = orig.sharedUserId;
- copyMimeGroups(orig.mimeGroups);
- pkgState = orig.pkgState;
- mDomainSetId = orig.getDomainSetId();
- mSnapshot = new SnapshotCache.Sealed();
+ PackageSetting(@NonNull PackageSetting original, boolean sealedSnapshot) {
+ super(original);
+ copyPackageSetting(original);
+ if (sealedSnapshot) {
+ sharedUser = sharedUser == null ? null : sharedUser.snapshot();
+ mSnapshot = new SnapshotCache.Sealed();
+ } else {
+ mSnapshot = makeCache();
+ }
}
/**
@@ -187,188 +288,11 @@
return mSnapshot.snapshot();
}
- /** @see #pkg **/
- @Nullable
- public AndroidPackage getPkg() {
- return pkg;
- }
-
- public Integer getSharedUserId() {
- if (sharedUser != null) {
- return sharedUser.userId;
- }
- return null;
- }
-
- public int getSharedUserIdInt() {
- if (sharedUser != null) {
- return sharedUser.userId;
- }
- return sharedUserId;
- }
-
- public SharedUserSetting getSharedUser() {
- return sharedUser;
- }
-
- @Override
- public String toString() {
- return "PackageSetting{"
- + Integer.toHexString(System.identityHashCode(this))
- + " " + name + "/" + appId + "}";
- }
-
- public void copyFrom(PackageSetting orig) {
- super.copyFrom(orig);
- doCopy(orig);
- }
-
- private void doCopy(PackageSetting orig) {
- appId = orig.appId;
- pkg = orig.pkg;
- sharedUser = orig.sharedUser;
- sharedUserId = orig.sharedUserId;
- copyMimeGroups(orig.mimeGroups);
- mDomainSetId = orig.getDomainSetId();
- }
-
- private void copyMimeGroups(@Nullable Map<String, ArraySet<String>> newMimeGroups) {
- if (newMimeGroups == null) {
- mimeGroups = null;
- return;
- }
-
- mimeGroups = new ArrayMap<>(newMimeGroups.size());
- for (String mimeGroup : newMimeGroups.keySet()) {
- ArraySet<String> mimeTypes = newMimeGroups.get(mimeGroup);
-
- if (mimeTypes != null) {
- mimeGroups.put(mimeGroup, new ArraySet<>(mimeTypes));
- } else {
- mimeGroups.put(mimeGroup, new ArraySet<>());
- }
- }
- }
-
- /**
- * Updates declared MIME groups, removing no longer declared groups
- * and keeping previous state of MIME groups
- */
- void updateMimeGroups(@Nullable Set<String> newMimeGroupNames) {
- if (newMimeGroupNames == null) {
- mimeGroups = null;
- return;
- }
-
- if (mimeGroups == null) {
- // set mimeGroups to empty map to avoid repeated null-checks in the next loop
- mimeGroups = Collections.emptyMap();
- }
-
- ArrayMap<String, ArraySet<String>> updatedMimeGroups =
- new ArrayMap<>(newMimeGroupNames.size());
-
- for (String mimeGroup : newMimeGroupNames) {
- if (mimeGroups.containsKey(mimeGroup)) {
- updatedMimeGroups.put(mimeGroup, mimeGroups.get(mimeGroup));
- } else {
- updatedMimeGroups.put(mimeGroup, new ArraySet<>());
- }
- }
- mimeGroups = updatedMimeGroups;
- }
-
- @Deprecated
- @Override
- public LegacyPermissionState getLegacyPermissionState() {
- return (sharedUser != null)
- ? sharedUser.getLegacyPermissionState()
- : super.getLegacyPermissionState();
- }
-
- public int getAppId() {
- return appId;
- }
-
- public void setInstallPermissionsFixed(boolean fixed) {
- installPermissionsFixed = fixed;
- }
-
- public boolean areInstallPermissionsFixed() {
- return installPermissionsFixed;
- }
-
- public boolean isPrivileged() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
- }
-
- public boolean isOem() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
- }
-
- public boolean isVendor() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
- }
-
- public boolean isProduct() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
- }
-
- public boolean isSystemExt() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0;
- }
-
- public boolean isOdm() {
- return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
- }
-
- public boolean isSystem() {
- return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
- }
-
- @Override
- public boolean isSharedUser() {
- return sharedUser != null;
- }
-
- public boolean isMatch(int flags) {
- if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
- return isSystem();
- }
- return true;
- }
-
- public boolean setMimeGroup(String mimeGroup, List<String> mimeTypes) {
- ArraySet<String> oldMimeTypes = getMimeGroupInternal(mimeGroup);
- if (oldMimeTypes == null) {
- throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
- + " for package " + name);
- }
-
- ArraySet<String> newMimeTypes = new ArraySet<>(mimeTypes);
- boolean hasChanges = !newMimeTypes.equals(oldMimeTypes);
- mimeGroups.put(mimeGroup, newMimeTypes);
- return hasChanges;
- }
-
- public List<String> getMimeGroup(String mimeGroup) {
- ArraySet<String> mimeTypes = getMimeGroupInternal(mimeGroup);
- if (mimeTypes == null) {
- throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
- + " for package " + name);
- }
- return new ArrayList<>(mimeTypes);
- }
-
- private ArraySet<String> getMimeGroupInternal(String mimeGroup) {
- return mimeGroups != null ? mimeGroups.get(mimeGroup) : null;
- }
-
public void dumpDebug(ProtoOutputStream proto, long fieldId, List<UserInfo> users,
LegacyPermissionDataProvider dataProvider) {
final long packageToken = proto.start(fieldId);
- proto.write(PackageProto.NAME, (realName != null ? realName : name));
- proto.write(PackageProto.UID, appId);
+ proto.write(PackageProto.NAME, (mRealName != null ? mRealName : mName));
+ proto.write(PackageProto.UID, mAppId);
proto.write(PackageProto.VERSION_CODE, versionCode);
proto.write(PackageProto.INSTALL_TIME_MS, firstInstallTime);
proto.write(PackageProto.UPDATE_TIME_MS, lastUpdateTime);
@@ -405,6 +329,728 @@
proto.end(packageToken);
}
+ public List<String> getMimeGroup(String mimeGroup) {
+ ArraySet<String> mimeTypes = getMimeGroupInternal(mimeGroup);
+ if (mimeTypes == null) {
+ throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
+ + " for package " + mName);
+ }
+ return new ArrayList<>(mimeTypes);
+ }
+
+ private ArraySet<String> getMimeGroupInternal(String mimeGroup) {
+ return mimeGroups != null ? mimeGroups.get(mimeGroup) : null;
+ }
+
+ public boolean isMatch(int flags) {
+ if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) {
+ return isSystem();
+ }
+ return true;
+ }
+
+ public boolean isSharedUser() {
+ return sharedUser != null;
+ }
+
+ public PackageSetting setAppId(int appId) {
+ this.mAppId = appId;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setCpuAbiOverride(String cpuAbiOverrideString) {
+ this.mCpuAbiOverride = cpuAbiOverrideString;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setFirstInstallTime(long firstInstallTime) {
+ this.firstInstallTime = firstInstallTime;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setForceQueryableOverride(boolean forceQueryableOverride) {
+ this.forceQueryableOverride = forceQueryableOverride;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setInstallerPackageName(String packageName) {
+ installSource = installSource.setInstallerPackage(packageName);
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setInstallSource(InstallSource installSource) {
+ this.installSource = Objects.requireNonNull(installSource);
+ onChanged();
+ return this;
+ }
+
+ PackageSetting removeInstallerPackage(String packageName) {
+ installSource = installSource.removeInstallerPackage(packageName);
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setIsOrphaned(boolean isOrphaned) {
+ installSource = installSource.setIsOrphaned(isOrphaned);
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setKeySetData(PackageKeySetData keySetData) {
+ this.keySetData = keySetData;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setLastModifiedTime(long timeStamp) {
+ this.mLastModifiedTime = timeStamp;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setLastUpdateTime(long lastUpdateTime) {
+ this.lastUpdateTime = lastUpdateTime;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setLongVersionCode(long versionCode) {
+ this.versionCode = versionCode;
+ onChanged();
+ return this;
+ }
+
+ public boolean setMimeGroup(String mimeGroup, List<String> mimeTypes) {
+ ArraySet<String> oldMimeTypes = getMimeGroupInternal(mimeGroup);
+ if (oldMimeTypes == null) {
+ throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
+ + " for package " + mName);
+ }
+
+ ArraySet<String> newMimeTypes = new ArraySet<>(mimeTypes);
+ boolean hasChanges = !newMimeTypes.equals(oldMimeTypes);
+ mimeGroups.put(mimeGroup, newMimeTypes);
+ if (hasChanges) {
+ onChanged();
+ }
+ return hasChanges;
+ }
+
+ public PackageSetting setPkg(AndroidPackage pkg) {
+ this.pkg = pkg;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setPrimaryCpuAbi(String primaryCpuAbiString) {
+ this.mPrimaryCpuAbi = primaryCpuAbiString;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setSecondaryCpuAbi(String secondaryCpuAbiString) {
+ this.mSecondaryCpuAbi = secondaryCpuAbiString;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setSignatures(PackageSignatures signatures) {
+ this.signatures = signatures;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setVolumeUuid(String volumeUuid) {
+ this.volumeUuid = volumeUuid;
+ onChanged();
+ return this;
+ }
+
+ @Override
+ public boolean isExternalStorage() {
+ return (pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
+ }
+
+ public PackageSetting setUpdateAvailable(boolean updateAvailable) {
+ this.updateAvailable = updateAvailable;
+ onChanged();
+ return this;
+ }
+
+ public int getSharedUserIdInt() {
+ if (sharedUser != null) {
+ return sharedUser.userId;
+ }
+ return sharedUserId;
+ }
+
+ @Override
+ public String toString() {
+ return "PackageSetting{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + mName + "/" + mAppId + "}";
+ }
+
+ protected void copyMimeGroups(@Nullable Map<String, ArraySet<String>> newMimeGroups) {
+ if (newMimeGroups == null) {
+ mimeGroups = null;
+ return;
+ }
+
+ mimeGroups = new ArrayMap<>(newMimeGroups.size());
+ for (String mimeGroup : newMimeGroups.keySet()) {
+ ArraySet<String> mimeTypes = newMimeGroups.get(mimeGroup);
+
+ if (mimeTypes != null) {
+ mimeGroups.put(mimeGroup, new ArraySet<>(mimeTypes));
+ } else {
+ mimeGroups.put(mimeGroup, new ArraySet<>());
+ }
+ }
+ }
+
+ /** Updates all fields in the current setting from another. */
+ public void updateFrom(PackageSetting other) {
+ copyPackageSetting(other);
+
+ Set<String> mimeGroupNames = other.mimeGroups != null ? other.mimeGroups.keySet() : null;
+ updateMimeGroups(mimeGroupNames);
+
+ onChanged();
+ }
+
+ /**
+ * Updates declared MIME groups, removing no longer declared groups
+ * and keeping previous state of MIME groups
+ */
+ PackageSetting updateMimeGroups(@Nullable Set<String> newMimeGroupNames) {
+ if (newMimeGroupNames == null) {
+ mimeGroups = null;
+ return this;
+ }
+
+ if (mimeGroups == null) {
+ // set mimeGroups to empty map to avoid repeated null-checks in the next loop
+ mimeGroups = Collections.emptyMap();
+ }
+
+ ArrayMap<String, ArraySet<String>> updatedMimeGroups =
+ new ArrayMap<>(newMimeGroupNames.size());
+
+ for (String mimeGroup : newMimeGroupNames) {
+ if (mimeGroups.containsKey(mimeGroup)) {
+ updatedMimeGroups.put(mimeGroup, mimeGroups.get(mimeGroup));
+ } else {
+ updatedMimeGroups.put(mimeGroup, new ArraySet<>());
+ }
+ }
+ onChanged();
+ mimeGroups = updatedMimeGroups;
+ return this;
+ }
+
+ @Deprecated
+ @Override
+ public LegacyPermissionState getLegacyPermissionState() {
+ return (sharedUser != null)
+ ? sharedUser.getLegacyPermissionState()
+ : super.getLegacyPermissionState();
+ }
+
+ public PackageSetting setInstallPermissionsFixed(boolean installPermissionsFixed) {
+ this.installPermissionsFixed = installPermissionsFixed;
+ return this;
+ }
+
+ public boolean isPrivileged() {
+ return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+ }
+
+ public boolean isOem() {
+ return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
+ }
+
+ public boolean isVendor() {
+ return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_VENDOR) != 0;
+ }
+
+ public boolean isProduct() {
+ return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
+ }
+
+ @Override
+ public boolean isRequiredForSystemUser() {
+ return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
+ }
+
+ public boolean isSystemExt() {
+ return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT) != 0;
+ }
+
+ public boolean isOdm() {
+ return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_ODM) != 0;
+ }
+
+ public boolean isSystem() {
+ return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ }
+
+ public SigningDetails getSigningDetails() {
+ return signatures.mSigningDetails;
+ }
+
+ public PackageSetting setSigningDetails(SigningDetails signingDetails) {
+ // TODO: Immutability
+ signatures.mSigningDetails = signingDetails;
+ onChanged();
+ return this;
+ }
+
+ public void copyPackageSetting(PackageSetting other) {
+ super.copySettingBase(other);
+ sharedUserId = other.sharedUserId;
+ mimeGroups = other.mimeGroups;
+ incrementalStates = other.incrementalStates;
+ legacyNativeLibraryPath = other.legacyNativeLibraryPath;
+ mName = other.mName;
+ mRealName = other.mRealName;
+ mAppId = other.mAppId;
+ pkg = other.pkg;
+ sharedUser = other.sharedUser;
+ mPath = other.mPath;
+ mPathString = other.mPathString;
+ mPrimaryCpuAbi = other.mPrimaryCpuAbi;
+ mSecondaryCpuAbi = other.mSecondaryCpuAbi;
+ mCpuAbiOverride = other.mCpuAbiOverride;
+ mLastModifiedTime = other.mLastModifiedTime;
+ firstInstallTime = other.firstInstallTime;
+ lastUpdateTime = other.lastUpdateTime;
+ versionCode = other.versionCode;
+ signatures = other.signatures;
+ installPermissionsFixed = other.installPermissionsFixed;
+ keySetData = other.keySetData;
+ installSource = other.installSource;
+ volumeUuid = other.volumeUuid;
+ categoryOverride = other.categoryOverride;
+ updateAvailable = other.updateAvailable;
+ forceQueryableOverride = other.forceQueryableOverride;
+ mDomainSetId = other.mDomainSetId;
+
+ usesStaticLibraries = other.usesStaticLibraries != null
+ ? Arrays.copyOf(other.usesStaticLibraries,
+ other.usesStaticLibraries.length) : null;
+ usesStaticLibrariesVersions = other.usesStaticLibrariesVersions != null
+ ? Arrays.copyOf(other.usesStaticLibrariesVersions,
+ other.usesStaticLibrariesVersions.length) : null;
+
+ mUserState.clear();
+ for (int i = 0; i < other.mUserState.size(); i++) {
+ mUserState.put(other.mUserState.keyAt(i), other.mUserState.valueAt(i));
+ }
+
+ if (mOldCodePaths != null) {
+ if (other.mOldCodePaths != null) {
+ mOldCodePaths.clear();
+ mOldCodePaths.addAll(other.mOldCodePaths);
+ } else {
+ mOldCodePaths = null;
+ }
+ }
+
+ copyMimeGroups(other.mimeGroups);
+ pkgState.updateFrom(other.pkgState);
+ onChanged();
+ }
+
+ @VisibleForTesting
+ PackageUserState modifyUserState(int userId) {
+ PackageUserState state = mUserState.get(userId);
+ if (state == null) {
+ state = new PackageUserState();
+ mUserState.put(userId, state);
+ onChanged();
+ }
+ return state;
+ }
+
+ @NonNull
+ public PackageUserState readUserState(int userId) {
+ PackageUserState state = mUserState.get(userId);
+ if (state == null) {
+ return DEFAULT_USER_STATE;
+ }
+ return state;
+ }
+
+ @Nullable
+ public PackageUserState readUserStateNullable(int userId) {
+ return mUserState.get(userId);
+ }
+
+ void setEnabled(int state, int userId, String callingPackage) {
+ PackageUserState st = modifyUserState(userId);
+ st.enabled = state;
+ st.lastDisableAppCaller = callingPackage;
+ onChanged();
+ }
+
+ int getEnabled(int userId) {
+ return readUserState(userId).enabled;
+ }
+
+ String getLastDisabledAppCaller(int userId) {
+ return readUserState(userId).lastDisableAppCaller;
+ }
+
+ void setInstalled(boolean inst, int userId) {
+ modifyUserState(userId).installed = inst;
+ }
+
+ boolean getInstalled(int userId) {
+ return readUserState(userId).installed;
+ }
+
+ int getInstallReason(int userId) {
+ return readUserState(userId).installReason;
+ }
+
+ void setInstallReason(int installReason, int userId) {
+ modifyUserState(userId).installReason = installReason;
+ }
+
+ int getUninstallReason(int userId) {
+ return readUserState(userId).uninstallReason;
+ }
+
+ void setUninstallReason(@PackageManager.UninstallReason int uninstallReason, int userId) {
+ modifyUserState(userId).uninstallReason = uninstallReason;
+ }
+
+ boolean setOverlayPaths(OverlayPaths overlayPaths, int userId) {
+ return modifyUserState(userId).setOverlayPaths(overlayPaths);
+ }
+
+ @NonNull
+ OverlayPaths getOverlayPaths(int userId) {
+ return readUserState(userId).getOverlayPaths();
+ }
+
+ boolean setOverlayPathsForLibrary(String libName, OverlayPaths overlayPaths, int userId) {
+ return modifyUserState(userId).setSharedLibraryOverlayPaths(libName, overlayPaths);
+ }
+
+ @NonNull
+ Map<String, OverlayPaths> getOverlayPathsForLibrary(int userId) {
+ return readUserState(userId).getSharedLibraryOverlayPaths();
+ }
+
+ /** Only use for testing. Do NOT use in production code. */
+ @VisibleForTesting
+ SparseArray<PackageUserState> getUserState() {
+ return mUserState;
+ }
+
+ boolean isAnyInstalled(int[] users) {
+ for (int user: users) {
+ if (readUserState(user).installed) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ int[] queryInstalledUsers(int[] users, boolean installed) {
+ int num = 0;
+ for (int user : users) {
+ if (getInstalled(user) == installed) {
+ num++;
+ }
+ }
+ int[] res = new int[num];
+ num = 0;
+ for (int user : users) {
+ if (getInstalled(user) == installed) {
+ res[num] = user;
+ num++;
+ }
+ }
+ return res;
+ }
+
+ long getCeDataInode(int userId) {
+ return readUserState(userId).ceDataInode;
+ }
+
+ void setCeDataInode(long ceDataInode, int userId) {
+ modifyUserState(userId).ceDataInode = ceDataInode;
+ }
+
+ boolean getStopped(int userId) {
+ return readUserState(userId).stopped;
+ }
+
+ void setStopped(boolean stop, int userId) {
+ modifyUserState(userId).stopped = stop;
+ }
+
+ boolean getNotLaunched(int userId) {
+ return readUserState(userId).notLaunched;
+ }
+
+ void setNotLaunched(boolean stop, int userId) {
+ modifyUserState(userId).notLaunched = stop;
+ }
+
+ boolean getHidden(int userId) {
+ return readUserState(userId).hidden;
+ }
+
+ void setHidden(boolean hidden, int userId) {
+ modifyUserState(userId).hidden = hidden;
+ }
+
+ int getDistractionFlags(int userId) {
+ return readUserState(userId).distractionFlags;
+ }
+
+ void setDistractionFlags(int distractionFlags, int userId) {
+ modifyUserState(userId).distractionFlags = distractionFlags;
+ }
+
+ boolean getSuspended(int userId) {
+ return readUserState(userId).suspended;
+ }
+
+ boolean isSuspendedBy(String suspendingPackage, int userId) {
+ final PackageUserState state = readUserState(userId);
+ return state.suspendParams != null && state.suspendParams.containsKey(suspendingPackage);
+ }
+
+ boolean addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
+ PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
+ final PackageUserState existingUserState = modifyUserState(userId);
+ final PackageUserState.SuspendParams newSuspendParams =
+ PackageUserState.SuspendParams.getInstanceOrNull(dialogInfo, appExtras,
+ launcherExtras);
+ if (existingUserState.suspendParams == null) {
+ existingUserState.suspendParams = new ArrayMap<>();
+ }
+ final PackageUserState.SuspendParams oldSuspendParams =
+ existingUserState.suspendParams.put(suspendingPackage, newSuspendParams);
+ existingUserState.suspended = true;
+ onChanged();
+ return !Objects.equals(oldSuspendParams, newSuspendParams);
+ }
+
+ boolean removeSuspension(String suspendingPackage, int userId) {
+ boolean wasModified = false;
+ final PackageUserState existingUserState = modifyUserState(userId);
+ if (existingUserState.suspendParams != null) {
+ if (existingUserState.suspendParams.remove(suspendingPackage) != null) {
+ wasModified = true;
+ }
+ if (existingUserState.suspendParams.size() == 0) {
+ existingUserState.suspendParams = null;
+ }
+ }
+ existingUserState.suspended = (existingUserState.suspendParams != null);
+ onChanged();
+ return wasModified;
+ }
+
+ void removeSuspension(Predicate<String> suspendingPackagePredicate, int userId) {
+ final PackageUserState existingUserState = modifyUserState(userId);
+ if (existingUserState.suspendParams != null) {
+ for (int i = existingUserState.suspendParams.size() - 1; i >= 0; i--) {
+ final String suspendingPackage = existingUserState.suspendParams.keyAt(i);
+ if (suspendingPackagePredicate.test(suspendingPackage)) {
+ existingUserState.suspendParams.removeAt(i);
+ }
+ }
+ if (existingUserState.suspendParams.size() == 0) {
+ existingUserState.suspendParams = null;
+ }
+ }
+ existingUserState.suspended = (existingUserState.suspendParams != null);
+ onChanged();
+ }
+
+ public boolean getInstantApp(int userId) {
+ return readUserState(userId).instantApp;
+ }
+
+ void setInstantApp(boolean instantApp, int userId) {
+ modifyUserState(userId).instantApp = instantApp;
+ }
+
+ boolean getVirtualPreload(int userId) {
+ return readUserState(userId).virtualPreload;
+ }
+
+ void setVirtualPreload(boolean virtualPreload, int userId) {
+ modifyUserState(userId).virtualPreload = virtualPreload;
+ }
+
+ void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
+ boolean notLaunched, boolean hidden, int distractionFlags, boolean suspended,
+ ArrayMap<String, PackageUserState.SuspendParams> suspendParams, boolean instantApp,
+ boolean virtualPreload, String lastDisableAppCaller,
+ ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
+ int installReason, int uninstallReason,
+ String harmfulAppWarning, String splashScreenTheme) {
+ PackageUserState state = modifyUserState(userId);
+ state.ceDataInode = ceDataInode;
+ state.enabled = enabled;
+ state.installed = installed;
+ state.stopped = stopped;
+ state.notLaunched = notLaunched;
+ state.hidden = hidden;
+ state.distractionFlags = distractionFlags;
+ state.suspended = suspended;
+ state.suspendParams = suspendParams;
+ state.lastDisableAppCaller = lastDisableAppCaller;
+ state.enabledComponents = enabledComponents;
+ state.disabledComponents = disabledComponents;
+ state.installReason = installReason;
+ state.uninstallReason = uninstallReason;
+ state.instantApp = instantApp;
+ state.virtualPreload = virtualPreload;
+ state.harmfulAppWarning = harmfulAppWarning;
+ state.splashScreenTheme = splashScreenTheme;
+ onChanged();
+ }
+
+ void setUserState(int userId, PackageUserState otherState) {
+ setUserState(userId, otherState.ceDataInode, otherState.enabled, otherState.installed,
+ otherState.stopped, otherState.notLaunched, otherState.hidden,
+ otherState.distractionFlags, otherState.suspended, otherState.suspendParams,
+ otherState.instantApp,
+ otherState.virtualPreload, otherState.lastDisableAppCaller,
+ otherState.enabledComponents, otherState.disabledComponents,
+ otherState.installReason, otherState.uninstallReason, otherState.harmfulAppWarning,
+ otherState.splashScreenTheme);
+ }
+
+ ArraySet<String> getEnabledComponents(int userId) {
+ return readUserState(userId).enabledComponents;
+ }
+
+ ArraySet<String> getDisabledComponents(int userId) {
+ return readUserState(userId).disabledComponents;
+ }
+
+ void setEnabledComponents(ArraySet<String> components, int userId) {
+ modifyUserState(userId).enabledComponents = components;
+ }
+
+ void setDisabledComponents(ArraySet<String> components, int userId) {
+ modifyUserState(userId).disabledComponents = components;
+ }
+
+ void setEnabledComponentsCopy(ArraySet<String> components, int userId) {
+ modifyUserState(userId).enabledComponents = components != null
+ ? new ArraySet<String>(components) : null;
+ }
+
+ void setDisabledComponentsCopy(ArraySet<String> components, int userId) {
+ modifyUserState(userId).disabledComponents = components != null
+ ? new ArraySet<String>(components) : null;
+ }
+
+ PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) {
+ PackageUserState state = modifyUserState(userId);
+ boolean changed = false;
+ if (disabled && state.disabledComponents == null) {
+ state.disabledComponents = new ArraySet<String>(1);
+ changed = true;
+ }
+ if (enabled && state.enabledComponents == null) {
+ state.enabledComponents = new ArraySet<String>(1);
+ changed = true;
+ }
+ if (changed) {
+ onChanged();
+ }
+ return state;
+ }
+
+ void addDisabledComponent(String componentClassName, int userId) {
+ modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName);
+ }
+
+ void addEnabledComponent(String componentClassName, int userId) {
+ modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName);
+ }
+
+ boolean enableComponentLPw(String componentClassName, int userId) {
+ PackageUserState state = modifyUserStateComponents(userId, false, true);
+ boolean changed = state.disabledComponents != null
+ ? state.disabledComponents.remove(componentClassName) : false;
+ changed |= state.enabledComponents.add(componentClassName);
+ return changed;
+ }
+
+ boolean disableComponentLPw(String componentClassName, int userId) {
+ PackageUserState state = modifyUserStateComponents(userId, true, false);
+ boolean changed = state.enabledComponents != null
+ ? state.enabledComponents.remove(componentClassName) : false;
+ changed |= state.disabledComponents.add(componentClassName);
+ return changed;
+ }
+
+ boolean restoreComponentLPw(String componentClassName, int userId) {
+ PackageUserState state = modifyUserStateComponents(userId, true, true);
+ boolean changed = state.disabledComponents != null
+ ? state.disabledComponents.remove(componentClassName) : false;
+ changed |= state.enabledComponents != null
+ ? state.enabledComponents.remove(componentClassName) : false;
+ return changed;
+ }
+
+ int getCurrentEnabledStateLPr(String componentName, int userId) {
+ PackageUserState state = readUserState(userId);
+ if (state.enabledComponents != null && state.enabledComponents.contains(componentName)) {
+ return COMPONENT_ENABLED_STATE_ENABLED;
+ } else if (state.disabledComponents != null
+ && state.disabledComponents.contains(componentName)) {
+ return COMPONENT_ENABLED_STATE_DISABLED;
+ } else {
+ return COMPONENT_ENABLED_STATE_DEFAULT;
+ }
+ }
+
+ void removeUser(int userId) {
+ mUserState.delete(userId);
+ onChanged();
+ }
+
+ public int[] getNotInstalledUserIds() {
+ int count = 0;
+ int userStateCount = mUserState.size();
+ for (int i = 0; i < userStateCount; i++) {
+ if (!mUserState.valueAt(i).installed) {
+ count++;
+ }
+ }
+ if (count == 0) {
+ return EmptyArray.INT;
+ }
+
+ int[] excludedUserIds = new int[count];
+ int idx = 0;
+ for (int i = 0; i < userStateCount; i++) {
+ if (!mUserState.valueAt(i).installed) {
+ excludedUserIds[idx++] = mUserState.keyAt(i);
+ }
+ }
+ return excludedUserIds;
+ }
+
/**
* TODO (b/170263003) refactor to dump to permissiongr proto
* Dumps the permissions that are granted to users for this package.
@@ -416,7 +1062,7 @@
final long permissionsToken = proto.start(PackageProto.USER_PERMISSIONS);
proto.write(PackageProto.UserPermissionsProto.ID, user.id);
- runtimePermissionStates = dataProvider.getLegacyPermissionState(appId)
+ runtimePermissionStates = dataProvider.getLegacyPermissionState(mAppId)
.getPermissionStates(user.id);
for (LegacyPermissionState.PermissionState permission : runtimePermissionStates) {
if (permission.isGranted()) {
@@ -428,37 +1074,473 @@
}
}
- /** Updates all fields in the current setting from another. */
- public void updateFrom(PackageSetting other) {
- super.updateFrom(other);
- appId = other.appId;
- pkg = other.pkg;
- sharedUserId = other.sharedUserId;
- sharedUser = other.sharedUser;
- mDomainSetId = other.mDomainSetId;
+ protected void writeUsersInfoToProto(ProtoOutputStream proto, long fieldId) {
+ int count = mUserState.size();
+ for (int i = 0; i < count; i++) {
+ final long userToken = proto.start(fieldId);
+ final int userId = mUserState.keyAt(i);
+ final PackageUserState state = mUserState.valueAt(i);
+ proto.write(PackageProto.UserInfoProto.ID, userId);
+ final int installType;
+ if (state.instantApp) {
+ installType = PackageProto.UserInfoProto.INSTANT_APP_INSTALL;
+ } else if (state.installed) {
+ installType = PackageProto.UserInfoProto.FULL_APP_INSTALL;
+ } else {
+ installType = PackageProto.UserInfoProto.NOT_INSTALLED_FOR_USER;
+ }
+ proto.write(PackageProto.UserInfoProto.INSTALL_TYPE, installType);
+ proto.write(PackageProto.UserInfoProto.IS_HIDDEN, state.hidden);
+ proto.write(PackageProto.UserInfoProto.DISTRACTION_FLAGS, state.distractionFlags);
+ proto.write(PackageProto.UserInfoProto.IS_SUSPENDED, state.suspended);
+ if (state.suspended) {
+ for (int j = 0; j < state.suspendParams.size(); j++) {
+ proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE,
+ state.suspendParams.keyAt(j));
+ }
+ }
+ proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.stopped);
+ proto.write(PackageProto.UserInfoProto.IS_LAUNCHED, !state.notLaunched);
+ proto.write(PackageProto.UserInfoProto.ENABLED_STATE, state.enabled);
+ proto.write(
+ PackageProto.UserInfoProto.LAST_DISABLED_APP_CALLER,
+ state.lastDisableAppCaller);
+ proto.end(userToken);
+ }
+ }
- Set<String> mimeGroupNames = other.mimeGroups != null ? other.mimeGroups.keySet() : null;
- updateMimeGroups(mimeGroupNames);
+ void setHarmfulAppWarning(int userId, String harmfulAppWarning) {
+ PackageUserState userState = modifyUserState(userId);
+ userState.harmfulAppWarning = harmfulAppWarning;
+ onChanged();
+ }
- getPkgState().updateFrom(other.getPkgState());
+ String getHarmfulAppWarning(int userId) {
+ PackageUserState userState = readUserState(userId);
+ return userState.harmfulAppWarning;
+ }
+
+ /**
+ * @see #mPath
+ */
+ PackageSetting setPath(@NonNull File path) {
+ this.mPath = path;
+ this.mPathString = path.toString();
+ return this;
+ }
+
+ /** @see #mPath */
+ String getPathString() {
+ return mPathString;
+ }
+
+ /**
+ * @see PackageUserState#overrideLabelAndIcon(ComponentName, String, Integer)
+ *
+ * @param userId the specific user to change the label/icon for
+ */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public boolean overrideNonLocalizedLabelAndIcon(@NonNull ComponentName component,
+ @Nullable String label, @Nullable Integer icon, @UserIdInt int userId) {
+ return modifyUserState(userId).overrideLabelAndIcon(component, label, icon);
+ }
+
+ /**
+ * @see PackageUserState#resetOverrideComponentLabelIcon()
+ *
+ * @param userId the specific user to reset
+ */
+ public void resetOverrideComponentLabelIcon(@UserIdInt int userId) {
+ modifyUserState(userId).resetOverrideComponentLabelIcon();
+ }
+
+ /**
+ * @param userId the specified user to modify the theme for
+ * @param themeName the theme name to persist
+ * @see android.window.SplashScreen#setSplashScreenTheme(int)
+ */
+ public void setSplashScreenTheme(@UserIdInt int userId, @Nullable String themeName) {
+ modifyUserState(userId).splashScreenTheme = themeName;
+ onChanged();
+ }
+
+ /**
+ * @param userId the specified user to get the theme setting from
+ * @return the theme name previously persisted for the user or null
+ * if no splashscreen theme is persisted.
+ * @see android.window.SplashScreen#setSplashScreenTheme(int)
+ */
+ @Nullable
+ public String getSplashScreenTheme(@UserIdInt int userId) {
+ return readUserState(userId).splashScreenTheme;
+ }
+
+ /**
+ * @return True if package is still being loaded, false if the package is fully loaded.
+ */
+ public boolean isPackageLoading() {
+ return incrementalStates.getIncrementalStatesInfo().isLoading();
+ }
+
+ /**
+ * @return all current states in a Parcelable.
+ */
+ public IncrementalStatesInfo getIncrementalStatesInfo() {
+ return incrementalStates.getIncrementalStatesInfo();
+ }
+
+ /**
+ * Called to indicate that the package installation has been committed. This will create a
+ * new startable state and a new loading state with default values. By default, the package is
+ * startable after commit. For a package installed on Incremental, the loading state is true.
+ * For non-Incremental packages, the loading state is false.
+ */
+ public void setStatesOnCommit() {
+ incrementalStates.onCommit(IncrementalManager.isIncrementalPath(getPathString()));
+ }
+
+ /**
+ * Called to set the callback to listen for startable state changes.
+ */
+ public void setIncrementalStatesCallback(IncrementalStates.Callback callback) {
+ incrementalStates.setCallback(callback);
+ }
+
+ /**
+ * Called to report progress changes. This might trigger loading state change.
+ * @see IncrementalStates#setProgress(float)
+ */
+ public void setLoadingProgress(float progress) {
+ incrementalStates.setProgress(progress);
}
@NonNull
- public PackageStateUnserialized getPkgState() {
- return pkgState;
+ @Override
+ public long getLongVersionCode() {
+ return versionCode;
+ }
+
+ @Nullable
+ @Override
+ public Map<String, Set<String>> getMimeGroups() {
+ return CollectionUtils.isEmpty(mimeGroups) ? Collections.emptyMap()
+ : Collections.unmodifiableMap(mimeGroups);
}
@NonNull
- public UUID getDomainSetId() {
- return mDomainSetId;
+ @Override
+ public String getPackageName() {
+ return mName;
+ }
+
+ @Nullable
+ @Override
+ public AndroidPackageApi getAndroidPackage() {
+ return getPkg();
+ }
+
+ @Nullable
+ @Override
+ public Integer getSharedUserId() {
+ return sharedUser == null ? null : sharedUser.userId;
+ }
+
+ @NonNull
+ public SigningInfo getSigningInfo() {
+ return new SigningInfo(signatures.mSigningDetails);
+ }
+
+ @NonNull
+ @Override
+ public String[] getUsesStaticLibraries() {
+ return usesStaticLibraries == null ? EmptyArray.STRING : usesStaticLibraries;
+ }
+
+ @NonNull
+ @Override
+ public long[] getUsesStaticLibrariesVersions() {
+ return usesStaticLibrariesVersions == null ? EmptyArray.LONG : usesStaticLibrariesVersions;
+ }
+
+ @NonNull
+ @Override
+ public List<SharedLibraryInfo> getUsesLibraryInfos() {
+ return pkgState.getUsesLibraryInfos();
+ }
+
+ @NonNull
+ @Override
+ public List<String> getUsesLibraryFiles() {
+ return pkgState.getUsesLibraryFiles();
+ }
+
+ @Override
+ public boolean isHiddenUntilInstalled() {
+ return pkgState.isHiddenUntilInstalled();
+ }
+
+ @Nullable
+ @Override
+ public String getSeInfoOverride() {
+ return pkgState.getOverrideSeInfo();
+ }
+
+ @NonNull
+ @Override
+ public long[] getLastPackageUsageTime() {
+ return pkgState.getLastPackageUsageTimeInMills();
+ }
+
+ @Override
+ public boolean isUpdatedSystemApp() {
+ return pkgState.isUpdatedSystemApp();
+ }
+
+ @Deprecated
+ @Override
+ public int[] getUserIds() {
+ int size = mUserState.size();
+ int[] array = new int[size];
+ for (int index = 0; index < size; index++) {
+ array[index] = mUserState.keyAt(index);
+ }
+ return array;
+ }
+
+ @Override
+ public PackageUserState getUserState(int userId) {
+ return readUserStateNullable(userId);
}
public PackageSetting setDomainSetId(@NonNull UUID domainSetId) {
mDomainSetId = domainSetId;
+ onChanged();
return this;
}
- public String getPackageName() {
- return name;
+ public PackageSetting setSharedUser(SharedUserSetting sharedUser) {
+ this.sharedUser = sharedUser;
+ onChanged();
+ return this;
}
+
+ public PackageSetting setCategoryOverride(int categoryHint) {
+ this.categoryOverride = categoryHint;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setLegacyNativeLibraryPath(
+ String legacyNativeLibraryPathString) {
+ this.legacyNativeLibraryPath = legacyNativeLibraryPathString;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setIncrementalStates(
+ IncrementalStates incrementalStates) {
+ this.incrementalStates = incrementalStates;
+ onChanged();
+ return this;
+ }
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Non-persisted value. During an "upgrade without restart", we need the set
+ * of all previous code paths so we can surgically add the new APKs to the
+ * active classloader. If at any point an application is upgraded with a
+ * restart, this field will be cleared since the classloader would be created
+ * using the full set of code paths when the package's process is started.
+ * TODO: Remove
+ */
+ @DataClass.Generated.Member
+ public @Deprecated @Nullable Set<String> getOldCodePaths() {
+ return mOldCodePaths;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull IncrementalStates getIncrementalStates() {
+ return incrementalStates;
+ }
+
+ /**
+ * The path under which native libraries have been unpacked. This path is
+ * always derived at runtime, and is only stored here for cleanup when a
+ * package is uninstalled.
+ */
+ @DataClass.Generated.Member
+ public @Nullable @Deprecated String getLegacyNativeLibraryPath() {
+ return legacyNativeLibraryPath;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull String getName() {
+ return mName;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull String getRealName() {
+ return mRealName;
+ }
+
+ @DataClass.Generated.Member
+ public int getAppId() {
+ return mAppId;
+ }
+
+ /**
+ * It is expected that all code that uses a {@link PackageSetting} understands this inner field
+ * may be null. Note that this relationship only works one way. It should not be possible to
+ * have an entry inside {@link PackageManagerService#mPackages} without a corresponding
+ * {@link PackageSetting} inside {@link Settings#mPackages}.
+ *
+ * @see PackageState#getAndroidPackage()
+ */
+ @DataClass.Generated.Member
+ public @Nullable AndroidPackage getPkg() {
+ return pkg;
+ }
+
+ /**
+ * WARNING. The object reference is important. We perform integer equality and NOT
+ * object equality to check whether shared user settings are the same.
+ */
+ @DataClass.Generated.Member
+ public @Nullable SharedUserSetting getSharedUser() {
+ return sharedUser;
+ }
+
+ /**
+ * @see AndroidPackage#getPath()
+ */
+ @DataClass.Generated.Member
+ public @NonNull File getPath() {
+ return mPath;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String getPrimaryCpuAbi() {
+ return mPrimaryCpuAbi;
+ }
+
+ @DataClass.Generated.Member
+ public @Nullable String getSecondaryCpuAbi() {
+ return mSecondaryCpuAbi;
+ }
+
+ /**
+ * The install time CPU override, if any. This value is written at install time
+ * and doesn't change during the life of an install. If non-null,
+ * {@code primaryCpuAbiString} will contain the same value.
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getCpuAbiOverride() {
+ return mCpuAbiOverride;
+ }
+
+ @DataClass.Generated.Member
+ public long getLastModifiedTime() {
+ return mLastModifiedTime;
+ }
+
+ @DataClass.Generated.Member
+ public long getFirstInstallTime() {
+ return firstInstallTime;
+ }
+
+ @DataClass.Generated.Member
+ public long getLastUpdateTime() {
+ return lastUpdateTime;
+ }
+
+ @DataClass.Generated.Member
+ public long getVersionCode() {
+ return versionCode;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageSignatures getSignatures() {
+ return signatures;
+ }
+
+ @DataClass.Generated.Member
+ public boolean isInstallPermissionsFixed() {
+ return installPermissionsFixed;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageKeySetData getKeySetData() {
+ return keySetData;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull InstallSource getInstallSource() {
+ return installSource;
+ }
+
+ /**
+ * @see PackageState#getVolumeUuid()
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getVolumeUuid() {
+ return volumeUuid;
+ }
+
+ /**
+ * @see PackageState#getCategoryOverride()
+ */
+ @DataClass.Generated.Member
+ public int getCategoryOverride() {
+ return categoryOverride;
+ }
+
+ /**
+ * @see PackageState#isUpdateAvailable()
+ */
+ @DataClass.Generated.Member
+ public boolean isUpdateAvailable() {
+ return updateAvailable;
+ }
+
+ @DataClass.Generated.Member
+ public boolean isForceQueryableOverride() {
+ return forceQueryableOverride;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull PackageStateUnserialized getPkgState() {
+ return pkgState;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull UUID getDomainSetId() {
+ return mDomainSetId;
+ }
+
+ @DataClass.Generated(
+ time = 1628017546382L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
+ inputSignatures = "static final android.content.pm.PackageUserState DEFAULT_USER_STATE\nprotected int sharedUserId\n @android.annotation.Nullable java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>> mimeGroups\n @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.NonNull com.android.server.pm.IncrementalStates incrementalStates\n @android.annotation.Nullable java.lang.String[] usesStaticLibraries\n @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long firstInstallTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<android.content.pm.PackageUserState> mUserState\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic java.util.List<java.lang.String> getMimeGroup(java.lang.String)\nprivate android.util.ArraySet<java.lang.String> getMimeGroupInternal(java.lang.String)\npublic boolean isMatch(int)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,java.util.List<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,android.util.ArraySet<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyFrom(com.android.server.pm.PackageSetting)\nprivate void doCopy(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting android.content.pm.PackageUserState modifyUserState(int)\npublic @android.annotation.NonNull android.content.pm.PackageUserState readUserState(int)\npublic @android.annotation.Nullable android.content.pm.PackageUserState readUserStateNullable(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n @com.android.internal.annotations.VisibleForTesting android.util.SparseArray<android.content.pm.PackageUserState> getUserState()\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\n boolean getSuspended(int)\n boolean isSuspendedBy(java.lang.String,int)\n void addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n void removeSuspension(java.lang.String,int)\n void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,boolean,android.util.ArrayMap<java.lang.String,android.content.pm.PackageUserState.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String)\n void setUserState(int,android.content.pm.PackageUserState)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n android.content.pm.PackageUserState modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\n java.lang.String getPathString()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isPackageLoading()\npublic android.content.pm.IncrementalStatesInfo getIncrementalStatesInfo()\npublic void setStatesOnCommit()\npublic void setIncrementalStatesCallback(com.android.server.pm.IncrementalStates.Callback)\npublic void setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getLongVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.Nullable @java.lang.Override java.lang.String getSeInfoOverride()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Deprecated @java.lang.Override int[] getUserIds()\npublic @java.lang.Override android.content.pm.PackageUserState getUserState(int)\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setIncrementalStates(com.android.server.pm.IncrementalStates)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageState]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
deleted file mode 100644
index 62c8477..0000000
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ /dev/null
@@ -1,892 +0,0 @@
-/*
- * Copyright (C) 2011 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.pm;
-
-import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
-import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
-import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.content.ComponentName;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.IncrementalStatesInfo;
-import android.content.pm.PackageManager.UninstallReason;
-import android.content.pm.PackageUserState;
-import android.content.pm.Signature;
-import android.content.pm.SigningDetails;
-import android.content.pm.SuspendDialogInfo;
-import android.content.pm.overlay.OverlayPaths;
-import android.os.PersistableBundle;
-import android.os.incremental.IncrementalManager;
-import android.service.pm.PackageProto;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.SparseArray;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
-import com.android.server.pm.pkg.PackageState;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.Predicate;
-
-/**
- * Settings base class for pending and resolved classes.
- */
-public abstract class PackageSettingBase extends SettingBase implements PackageState {
-
- private static final int[] EMPTY_INT_ARRAY = new int[0];
-
- public final String name;
- final String realName;
-
- /** @see AndroidPackage#getPath() */
- private File mPath;
- private String mPathString;
-
- String[] usesStaticLibraries;
- long[] usesStaticLibrariesVersions;
-
- /**
- * The path under which native libraries have been unpacked. This path is
- * always derived at runtime, and is only stored here for cleanup when a
- * package is uninstalled.
- */
- @Deprecated
- String legacyNativeLibraryPathString;
-
- /**
- * The primary CPU abi for this package.
- */
- public String primaryCpuAbiString;
-
- /**
- * The secondary CPU abi for this package.
- */
- public String secondaryCpuAbiString;
-
- /**
- * The install time CPU override, if any. This value is written at install time
- * and doesn't change during the life of an install. If non-null,
- * {@code primaryCpuAbiString} will contain the same value.
- */
- String cpuAbiOverrideString;
-
- long timeStamp;
- long firstInstallTime;
- long lastUpdateTime;
- long versionCode;
-
- boolean uidError;
-
- PackageSignatures signatures;
-
- boolean installPermissionsFixed;
-
- PackageKeySetData keySetData = new PackageKeySetData();
-
- static final PackageUserState DEFAULT_USER_STATE = new PackageUserState();
-
- // Whether this package is currently stopped, thus can not be
- // started until explicitly launched by the user.
- private final SparseArray<PackageUserState> mUserState = new SparseArray<>();
-
- /**
- * Non-persisted value. During an "upgrade without restart", we need the set
- * of all previous code paths so we can surgically add the new APKs to the
- * active classloader. If at any point an application is upgraded with a
- * restart, this field will be cleared since the classloader would be created
- * using the full set of code paths when the package's process is started.
- */
- Set<String> mOldCodePaths;
-
- /** Information about how this package was installed/updated. */
- @NonNull InstallSource installSource;
- /** UUID of {@link VolumeInfo} hosting this app */
- String volumeUuid;
- /** The category of this app, as hinted by the installer */
- int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED;
- /** Whether or not an update is available. Ostensibly only for instant apps. */
- boolean updateAvailable;
-
- boolean forceQueryableOverride;
-
- @NonNull
- public IncrementalStates incrementalStates;
-
- PackageSettingBase(String name, String realName, @NonNull File path,
- String legacyNativeLibraryPathString, String primaryCpuAbiString,
- String secondaryCpuAbiString, String cpuAbiOverrideString,
- long pVersionCode, int pkgFlags, int pkgPrivateFlags,
- String[] usesStaticLibraries, long[] usesStaticLibrariesVersions) {
- super(pkgFlags, pkgPrivateFlags);
- this.name = name;
- this.realName = realName;
- this.usesStaticLibraries = usesStaticLibraries;
- this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
- setPath(path);
- this.legacyNativeLibraryPathString = legacyNativeLibraryPathString;
- this.primaryCpuAbiString = primaryCpuAbiString;
- this.secondaryCpuAbiString = secondaryCpuAbiString;
- this.cpuAbiOverrideString = cpuAbiOverrideString;
- this.versionCode = pVersionCode;
- this.signatures = new PackageSignatures();
- this.installSource = InstallSource.EMPTY;
- this.incrementalStates = new IncrementalStates();
- }
-
- /**
- * New instance of PackageSetting with one-level-deep cloning.
- * <p>
- * IMPORTANT: With a shallow copy, we do NOT create new contained objects.
- * This means, for example, changes to the user state of the original PackageSetting
- * will also change the user state in its copy.
- */
- PackageSettingBase(PackageSettingBase base, String realName) {
- super(base);
- name = base.name;
- this.realName = realName;
- doCopy(base);
- }
-
- // A copy constructor used to create snapshots. The boolean is present only to
- // match up with the constructor in PackageSetting.
- PackageSettingBase(PackageSettingBase orig, boolean snapshot) {
- super(orig);
- name = orig.name;
- realName = orig.realName;
- doCopy(orig);
- // Clone the user states.
- for (int i = 0; i < mUserState.size(); i++) {
- mUserState.put(mUserState.keyAt(i), new PackageUserState(mUserState.valueAt(i)));
- }
- }
-
- public void setInstallerPackageName(String packageName) {
- installSource = installSource.setInstallerPackage(packageName);
- onChanged();
- }
-
- public void setInstallSource(InstallSource installSource) {
- this.installSource = Objects.requireNonNull(installSource);
- onChanged();
- }
-
- void removeInstallerPackage(String packageName) {
- installSource = installSource.removeInstallerPackage(packageName);
- onChanged();
- }
-
- public void setIsOrphaned(boolean isOrphaned) {
- installSource = installSource.setIsOrphaned(isOrphaned);
- onChanged();
- }
-
- public void setVolumeUuid(String volumeUuid) {
- this.volumeUuid = volumeUuid;
- onChanged();
- }
-
- public String getVolumeUuid() {
- return volumeUuid;
- }
-
- public void setTimeStamp(long newStamp) {
- timeStamp = newStamp;
- onChanged();
- }
-
- public void setUpdateAvailable(boolean updateAvailable) {
- this.updateAvailable = updateAvailable;
- onChanged();
- }
-
- public boolean isUpdateAvailable() {
- return updateAvailable;
- }
-
- public boolean isSharedUser() {
- return false;
- }
-
- public Signature[] getSignatures() {
- return signatures.mSigningDetails.getSignatures();
- }
-
- public SigningDetails getSigningDetails() {
- return signatures.mSigningDetails;
- }
-
- /**
- * Makes a shallow copy of the given package settings.
- *
- * NOTE: For some fields [such as keySetData, signatures, mUserState, verificationInfo, etc...],
- * the original object is copied and a new one is not created.
- */
- public void copyFrom(PackageSettingBase orig) {
- super.copyFrom(orig);
- doCopy(orig);
- }
-
- private void doCopy(PackageSettingBase orig) {
- setPath(orig.getPath());
- cpuAbiOverrideString = orig.cpuAbiOverrideString;
- firstInstallTime = orig.firstInstallTime;
- installPermissionsFixed = orig.installPermissionsFixed;
- installSource = orig.installSource;
- keySetData = orig.keySetData;
- lastUpdateTime = orig.lastUpdateTime;
- legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString;
- // Intentionally skip mOldCodePaths; it's not relevant for copies
- primaryCpuAbiString = orig.primaryCpuAbiString;
- secondaryCpuAbiString = orig.secondaryCpuAbiString;
- signatures = orig.signatures;
- timeStamp = orig.timeStamp;
- uidError = orig.uidError;
- mUserState.clear();
- for (int i = 0; i < orig.mUserState.size(); i++) {
- mUserState.put(orig.mUserState.keyAt(i), orig.mUserState.valueAt(i));
- }
- versionCode = orig.versionCode;
- volumeUuid = orig.volumeUuid;
- categoryHint = orig.categoryHint;
- usesStaticLibraries = orig.usesStaticLibraries != null
- ? Arrays.copyOf(orig.usesStaticLibraries,
- orig.usesStaticLibraries.length) : null;
- usesStaticLibrariesVersions = orig.usesStaticLibrariesVersions != null
- ? Arrays.copyOf(orig.usesStaticLibrariesVersions,
- orig.usesStaticLibrariesVersions.length) : null;
- updateAvailable = orig.updateAvailable;
- forceQueryableOverride = orig.forceQueryableOverride;
- incrementalStates = orig.incrementalStates;
- }
-
- @VisibleForTesting
- PackageUserState modifyUserState(int userId) {
- PackageUserState state = mUserState.get(userId);
- if (state == null) {
- state = new PackageUserState();
- mUserState.put(userId, state);
- onChanged();
- }
- return state;
- }
-
- public PackageUserState readUserState(int userId) {
- PackageUserState state = mUserState.get(userId);
- if (state == null) {
- return DEFAULT_USER_STATE;
- }
-// state.categoryHint = categoryHint;
- return state;
- }
-
- void setEnabled(int state, int userId, String callingPackage) {
- PackageUserState st = modifyUserState(userId);
- st.enabled = state;
- st.lastDisableAppCaller = callingPackage;
- onChanged();
- }
-
- int getEnabled(int userId) {
- return readUserState(userId).enabled;
- }
-
- String getLastDisabledAppCaller(int userId) {
- return readUserState(userId).lastDisableAppCaller;
- }
-
- void setInstalled(boolean inst, int userId) {
- modifyUserState(userId).installed = inst;
- onChanged();
- }
-
- boolean getInstalled(int userId) {
- return readUserState(userId).installed;
- }
-
- int getInstallReason(int userId) {
- return readUserState(userId).installReason;
- }
-
- void setInstallReason(int installReason, int userId) {
- modifyUserState(userId).installReason = installReason;
- onChanged();
- }
-
- int getUninstallReason(int userId) {
- return readUserState(userId).uninstallReason;
- }
-
- void setUninstallReason(@UninstallReason int uninstallReason, int userId) {
- modifyUserState(userId).uninstallReason = uninstallReason;
- onChanged();
- }
-
- boolean setOverlayPaths(OverlayPaths overlayPaths, int userId) {
- boolean returnValue = modifyUserState(userId).setOverlayPaths(overlayPaths);
- onChanged();
- return returnValue;
- }
-
- OverlayPaths getOverlayPaths(int userId) {
- return readUserState(userId).getOverlayPaths();
- }
-
- boolean setOverlayPathsForLibrary(String libName, OverlayPaths overlayPaths,
- int userId) {
- boolean returnValue = modifyUserState(userId)
- .setSharedLibraryOverlayPaths(libName, overlayPaths);
- onChanged();
- return returnValue;
- }
-
- Map<String, OverlayPaths> getOverlayPathsForLibrary(int userId) {
- return readUserState(userId).getSharedLibraryOverlayPaths();
- }
-
- /**
- * Only use for testing. Do NOT use in production code.
- */
- @VisibleForTesting
- @Deprecated
- public SparseArray<PackageUserState> getUserState() {
- return mUserState;
- }
-
- boolean isAnyInstalled(int[] users) {
- for (int user: users) {
- if (readUserState(user).installed) {
- return true;
- }
- }
- return false;
- }
-
- int[] queryInstalledUsers(int[] users, boolean installed) {
- int num = 0;
- for (int user : users) {
- if (getInstalled(user) == installed) {
- num++;
- }
- }
- int[] res = new int[num];
- num = 0;
- for (int user : users) {
- if (getInstalled(user) == installed) {
- res[num] = user;
- num++;
- }
- }
- return res;
- }
-
- long getCeDataInode(int userId) {
- return readUserState(userId).ceDataInode;
- }
-
- void setCeDataInode(long ceDataInode, int userId) {
- modifyUserState(userId).ceDataInode = ceDataInode;
- onChanged();
- }
-
- boolean getStopped(int userId) {
- return readUserState(userId).stopped;
- }
-
- void setStopped(boolean stop, int userId) {
- modifyUserState(userId).stopped = stop;
- onChanged();
- }
-
- boolean getNotLaunched(int userId) {
- return readUserState(userId).notLaunched;
- }
-
- void setNotLaunched(boolean stop, int userId) {
- modifyUserState(userId).notLaunched = stop;
- onChanged();
- }
-
- boolean getHidden(int userId) {
- return readUserState(userId).hidden;
- }
-
- void setHidden(boolean hidden, int userId) {
- modifyUserState(userId).hidden = hidden;
- onChanged();
- }
-
- int getDistractionFlags(int userId) {
- return readUserState(userId).distractionFlags;
- }
-
- void setDistractionFlags(int distractionFlags, int userId) {
- modifyUserState(userId).distractionFlags = distractionFlags;
- onChanged();
- }
-
- boolean getSuspended(int userId) {
- return readUserState(userId).suspended;
- }
-
- boolean isSuspendedBy(String suspendingPackage, int userId) {
- final PackageUserState state = readUserState(userId);
- return state.suspendParams != null && state.suspendParams.containsKey(suspendingPackage);
- }
-
- boolean addOrUpdateSuspension(String suspendingPackage, SuspendDialogInfo dialogInfo,
- PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
- final PackageUserState existingUserState = modifyUserState(userId);
- final PackageUserState.SuspendParams newSuspendParams =
- PackageUserState.SuspendParams.getInstanceOrNull(dialogInfo, appExtras,
- launcherExtras);
- if (existingUserState.suspendParams == null) {
- existingUserState.suspendParams = new ArrayMap<>();
- }
- final PackageUserState.SuspendParams oldSuspendParams =
- existingUserState.suspendParams.put(suspendingPackage, newSuspendParams);
- existingUserState.suspended = true;
- onChanged();
- return !Objects.equals(oldSuspendParams, newSuspendParams);
- }
-
- boolean removeSuspension(String suspendingPackage, int userId) {
- boolean wasModified = false;
- final PackageUserState existingUserState = modifyUserState(userId);
- if (existingUserState.suspendParams != null) {
- if (existingUserState.suspendParams.remove(suspendingPackage) != null) {
- wasModified = true;
- }
- if (existingUserState.suspendParams.size() == 0) {
- existingUserState.suspendParams = null;
- }
- }
- existingUserState.suspended = (existingUserState.suspendParams != null);
- onChanged();
- return wasModified;
- }
-
- void removeSuspension(Predicate<String> suspendingPackagePredicate, int userId) {
- final PackageUserState existingUserState = modifyUserState(userId);
- if (existingUserState.suspendParams != null) {
- for (int i = existingUserState.suspendParams.size() - 1; i >= 0; i--) {
- final String suspendingPackage = existingUserState.suspendParams.keyAt(i);
- if (suspendingPackagePredicate.test(suspendingPackage)) {
- existingUserState.suspendParams.removeAt(i);
- }
- }
- if (existingUserState.suspendParams.size() == 0) {
- existingUserState.suspendParams = null;
- }
- }
- existingUserState.suspended = (existingUserState.suspendParams != null);
- onChanged();
- }
-
- public boolean getInstantApp(int userId) {
- return readUserState(userId).instantApp;
- }
-
- void setInstantApp(boolean instantApp, int userId) {
- modifyUserState(userId).instantApp = instantApp;
- onChanged();
- }
-
- boolean getVirtulalPreload(int userId) {
- return readUserState(userId).virtualPreload;
- }
-
- void setVirtualPreload(boolean virtualPreload, int userId) {
- modifyUserState(userId).virtualPreload = virtualPreload;
- onChanged();
- }
-
- void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
- boolean notLaunched, boolean hidden, int distractionFlags, boolean suspended,
- ArrayMap<String, PackageUserState.SuspendParams> suspendParams, boolean instantApp,
- boolean virtualPreload, String lastDisableAppCaller,
- ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
- int installReason, int uninstallReason, String harmfulAppWarning,
- String splashScreenTheme) {
- PackageUserState state = modifyUserState(userId);
- state.ceDataInode = ceDataInode;
- state.enabled = enabled;
- state.installed = installed;
- state.stopped = stopped;
- state.notLaunched = notLaunched;
- state.hidden = hidden;
- state.distractionFlags = distractionFlags;
- state.suspended = suspended;
- state.suspendParams = suspendParams;
- state.lastDisableAppCaller = lastDisableAppCaller;
- state.enabledComponents = enabledComponents;
- state.disabledComponents = disabledComponents;
- state.installReason = installReason;
- state.uninstallReason = uninstallReason;
- state.instantApp = instantApp;
- state.virtualPreload = virtualPreload;
- state.harmfulAppWarning = harmfulAppWarning;
- state.splashScreenTheme = splashScreenTheme;
- onChanged();
- }
-
- void setUserState(int userId, PackageUserState otherState) {
- setUserState(userId, otherState.ceDataInode, otherState.enabled, otherState.installed,
- otherState.stopped, otherState.notLaunched, otherState.hidden,
- otherState.distractionFlags, otherState.suspended, otherState.suspendParams,
- otherState.instantApp,
- otherState.virtualPreload, otherState.lastDisableAppCaller,
- otherState.enabledComponents, otherState.disabledComponents,
- otherState.installReason, otherState.uninstallReason, otherState.harmfulAppWarning,
- otherState.splashScreenTheme);
- }
-
- ArraySet<String> getEnabledComponents(int userId) {
- return readUserState(userId).enabledComponents;
- }
-
- ArraySet<String> getDisabledComponents(int userId) {
- return readUserState(userId).disabledComponents;
- }
-
- void setEnabledComponents(ArraySet<String> components, int userId) {
- modifyUserState(userId).enabledComponents = components;
- onChanged();
- }
-
- void setDisabledComponents(ArraySet<String> components, int userId) {
- modifyUserState(userId).disabledComponents = components;
- onChanged();
- }
-
- void setEnabledComponentsCopy(ArraySet<String> components, int userId) {
- modifyUserState(userId).enabledComponents = components != null
- ? new ArraySet<String>(components) : null;
- onChanged();
- }
-
- void setDisabledComponentsCopy(ArraySet<String> components, int userId) {
- modifyUserState(userId).disabledComponents = components != null
- ? new ArraySet<String>(components) : null;
- onChanged();
- }
-
- PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) {
- PackageUserState state = modifyUserState(userId);
- boolean changed = false;
- if (disabled && state.disabledComponents == null) {
- state.disabledComponents = new ArraySet<String>(1);
- changed = true;
- }
- if (enabled && state.enabledComponents == null) {
- state.enabledComponents = new ArraySet<String>(1);
- changed = true;
- }
- if (changed) {
- onChanged();
- }
- return state;
- }
-
- void addDisabledComponent(String componentClassName, int userId) {
- modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName);
- onChanged();
- }
-
- void addEnabledComponent(String componentClassName, int userId) {
- modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName);
- onChanged();
- }
-
- boolean enableComponentLPw(String componentClassName, int userId) {
- PackageUserState state = modifyUserStateComponents(userId, false, true);
- boolean changed = state.disabledComponents != null
- ? state.disabledComponents.remove(componentClassName) : false;
- changed |= state.enabledComponents.add(componentClassName);
- if (changed) {
- onChanged();
- }
- return changed;
- }
-
- boolean disableComponentLPw(String componentClassName, int userId) {
- PackageUserState state = modifyUserStateComponents(userId, true, false);
- boolean changed = state.enabledComponents != null
- ? state.enabledComponents.remove(componentClassName) : false;
- changed |= state.disabledComponents.add(componentClassName);
- if (changed) {
- onChanged();
- }
- return changed;
- }
-
- boolean restoreComponentLPw(String componentClassName, int userId) {
- PackageUserState state = modifyUserStateComponents(userId, true, true);
- boolean changed = state.disabledComponents != null
- ? state.disabledComponents.remove(componentClassName) : false;
- changed |= state.enabledComponents != null
- ? state.enabledComponents.remove(componentClassName) : false;
- if (changed) {
- onChanged();
- }
- return changed;
- }
-
- int getCurrentEnabledStateLPr(String componentName, int userId) {
- PackageUserState state = readUserState(userId);
- if (state.enabledComponents != null && state.enabledComponents.contains(componentName)) {
- return COMPONENT_ENABLED_STATE_ENABLED;
- } else if (state.disabledComponents != null
- && state.disabledComponents.contains(componentName)) {
- return COMPONENT_ENABLED_STATE_DISABLED;
- } else {
- return COMPONENT_ENABLED_STATE_DEFAULT;
- }
- }
-
- void removeUser(int userId) {
- mUserState.delete(userId);
- onChanged();
- }
-
- public int[] getNotInstalledUserIds() {
- int count = 0;
- int userStateCount = mUserState.size();
- for (int i = 0; i < userStateCount; i++) {
- if (!mUserState.valueAt(i).installed) {
- count++;
- }
- }
- if (count == 0) return EMPTY_INT_ARRAY;
- int[] excludedUserIds = new int[count];
- int idx = 0;
- for (int i = 0; i < userStateCount; i++) {
- if (!mUserState.valueAt(i).installed) {
- excludedUserIds[idx++] = mUserState.keyAt(i);
- }
- }
- return excludedUserIds;
- }
-
- protected void writeUsersInfoToProto(ProtoOutputStream proto, long fieldId) {
- int count = mUserState.size();
- for (int i = 0; i < count; i++) {
- final long userToken = proto.start(fieldId);
- final int userId = mUserState.keyAt(i);
- final PackageUserState state = mUserState.valueAt(i);
- proto.write(PackageProto.UserInfoProto.ID, userId);
- final int installType;
- if (state.instantApp) {
- installType = PackageProto.UserInfoProto.INSTANT_APP_INSTALL;
- } else if (state.installed) {
- installType = PackageProto.UserInfoProto.FULL_APP_INSTALL;
- } else {
- installType = PackageProto.UserInfoProto.NOT_INSTALLED_FOR_USER;
- }
- proto.write(PackageProto.UserInfoProto.INSTALL_TYPE, installType);
- proto.write(PackageProto.UserInfoProto.IS_HIDDEN, state.hidden);
- proto.write(PackageProto.UserInfoProto.DISTRACTION_FLAGS, state.distractionFlags);
- proto.write(PackageProto.UserInfoProto.IS_SUSPENDED, state.suspended);
- if (state.suspended) {
- for (int j = 0; j < state.suspendParams.size(); j++) {
- proto.write(PackageProto.UserInfoProto.SUSPENDING_PACKAGE,
- state.suspendParams.keyAt(j));
- }
- }
- proto.write(PackageProto.UserInfoProto.IS_STOPPED, state.stopped);
- proto.write(PackageProto.UserInfoProto.IS_LAUNCHED, !state.notLaunched);
- proto.write(PackageProto.UserInfoProto.ENABLED_STATE, state.enabled);
- proto.write(
- PackageProto.UserInfoProto.LAST_DISABLED_APP_CALLER,
- state.lastDisableAppCaller);
- proto.end(userToken);
- }
- }
-
- void setHarmfulAppWarning(int userId, String harmfulAppWarning) {
- PackageUserState userState = modifyUserState(userId);
- userState.harmfulAppWarning = harmfulAppWarning;
- onChanged();
- }
-
- String getHarmfulAppWarning(int userId) {
- PackageUserState userState = readUserState(userId);
- return userState.harmfulAppWarning;
- }
-
- /**
- * @see #mPath
- */
- PackageSettingBase setPath(@NonNull File path) {
- this.mPath = path;
- this.mPathString = path.toString();
- onChanged();
- return this;
- }
-
- /** @see #mPath */
- public File getPath() {
- return mPath;
- }
-
- /** @see #mPath */
- String getPathString() {
- return mPathString;
- }
-
- /**
- * @see PackageUserState#overrideLabelAndIcon(ComponentName, String, Integer)
- *
- * @param userId the specific user to change the label/icon for
- */
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public boolean overrideNonLocalizedLabelAndIcon(@NonNull ComponentName component,
- @Nullable String label, @Nullable Integer icon, @UserIdInt int userId) {
- boolean returnValue = modifyUserState(userId).overrideLabelAndIcon(component, label, icon);
- onChanged();
- return returnValue;
- }
-
- /**
- * @see PackageUserState#resetOverrideComponentLabelIcon()
- *
- * @param userId the specific user to reset
- */
- public void resetOverrideComponentLabelIcon(@UserIdInt int userId) {
- modifyUserState(userId).resetOverrideComponentLabelIcon();
- onChanged();
- }
-
- /**
- * @param userId the specified user to modify the theme for
- * @param themeName the theme name to persist
- * @see android.window.SplashScreen#setSplashScreenTheme(int)
- */
- public void setSplashScreenTheme(@UserIdInt int userId, @Nullable String themeName) {
- modifyUserState(userId).splashScreenTheme = themeName;
- onChanged();
- }
-
- /**
- * @param userId the specified user to get the theme setting from
- * @return the theme name previously persisted for the user or null
- * if no splashscreen theme is persisted.
- * @see android.window.SplashScreen#setSplashScreenTheme(int)
- */
- @Nullable
- public String getSplashScreenTheme(@UserIdInt int userId) {
- return readUserState(userId).splashScreenTheme;
- }
-
- /**
- * @return True if package is still being loaded, false if the package is fully loaded.
- */
- public boolean isPackageLoading() {
- return getIncrementalStates().isLoading();
- }
-
- /**
- * @return all current states in a Parcelable.
- */
- public IncrementalStatesInfo getIncrementalStates() {
- return incrementalStates.getIncrementalStatesInfo();
- }
-
- /**
- * Called to indicate that the package installation has been committed. This will create a
- * new loading state with default values.
- * For a package installed on Incremental, the loading state is true.
- * For non-Incremental packages, the loading state is false.
- */
- public void setStatesOnCommit() {
- incrementalStates.onCommit(IncrementalManager.isIncrementalPath(getPathString()));
- onChanged();
- }
-
- /**
- * Called to set the callback to listen for loading state changes.
- */
- public void setIncrementalStatesCallback(IncrementalStates.Callback callback) {
- incrementalStates.setCallback(callback);
- onChanged();
- }
-
- /**
- * Called to report progress changes. This might trigger loading state change.
- * @see IncrementalStates#setProgress(float)
- */
- public void setLoadingProgress(float progress) {
- incrementalStates.setProgress(progress);
- onChanged();
- }
-
- public long getFirstInstallTime() {
- return firstInstallTime;
- }
-
- public String getName() {
- return name;
- }
-
- protected PackageSettingBase updateFrom(PackageSettingBase other) {
- super.copyFrom(other);
- setPath(other.getPath());
- this.usesStaticLibraries = other.usesStaticLibraries;
- this.usesStaticLibrariesVersions = other.usesStaticLibrariesVersions;
- this.legacyNativeLibraryPathString = other.legacyNativeLibraryPathString;
- this.primaryCpuAbiString = other.primaryCpuAbiString;
- this.secondaryCpuAbiString = other.secondaryCpuAbiString;
- this.cpuAbiOverrideString = other.cpuAbiOverrideString;
- this.timeStamp = other.timeStamp;
- this.firstInstallTime = other.firstInstallTime;
- this.lastUpdateTime = other.lastUpdateTime;
- this.versionCode = other.versionCode;
- this.uidError = other.uidError;
- this.signatures = other.signatures;
- this.installPermissionsFixed = other.installPermissionsFixed;
- this.keySetData = other.keySetData;
- this.installSource = other.installSource;
- this.volumeUuid = other.volumeUuid;
- this.categoryHint = other.categoryHint;
- this.updateAvailable = other.updateAvailable;
- this.forceQueryableOverride = other.forceQueryableOverride;
- this.incrementalStates = other.incrementalStates;
-
- if (mOldCodePaths != null) {
- if (other.mOldCodePaths != null) {
- mOldCodePaths.clear();
- mOldCodePaths.addAll(other.mOldCodePaths);
- } else {
- mOldCodePaths = null;
- }
- }
- mUserState.clear();
- for (int i = 0; i < other.mUserState.size(); i++) {
- mUserState.put(other.mUserState.keyAt(i), other.mUserState.valueAt(i));
- }
- onChanged();
- return this;
- }
-}
diff --git a/services/core/java/com/android/server/pm/PackageUsage.java b/services/core/java/com/android/server/pm/PackageUsage.java
index ec897798..f0b200c 100644
--- a/services/core/java/com/android/server/pm/PackageUsage.java
+++ b/services/core/java/com/android/server/pm/PackageUsage.java
@@ -24,8 +24,6 @@
import android.util.AtomicFile;
import android.util.Log;
-import com.android.server.pm.parsing.pkg.AndroidPackage;
-
import libcore.io.IoUtils;
import java.io.BufferedInputStream;
@@ -72,7 +70,7 @@
continue;
}
sb.setLength(0);
- sb.append(pkgSetting.name);
+ sb.append(pkgSetting.getPackageName());
for (long usageTimeInMillis : pkgSetting.getPkgState()
.getLastPackageUsageTimeInMills()) {
sb.append(' ');
diff --git a/services/core/java/com/android/server/pm/PendingPackageBroadcasts.java b/services/core/java/com/android/server/pm/PendingPackageBroadcasts.java
new file mode 100644
index 0000000..4e9a06a
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PendingPackageBroadcasts.java
@@ -0,0 +1,93 @@
+/*
+ * 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.pm;
+
+import android.util.ArrayMap;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+
+/**
+ * Set of pending broadcasts for aggregating enable/disable of components.
+ */
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public final class PendingPackageBroadcasts {
+
+ // for each user id, a map of <package name -> components within that package>
+ final SparseArray<ArrayMap<String, ArrayList<String>>> mUidMap;
+
+ public PendingPackageBroadcasts() {
+ mUidMap = new SparseArray<>(2);
+ }
+
+ public ArrayList<String> get(int userId, String packageName) {
+ ArrayMap<String, ArrayList<String>> packages = getOrAllocate(userId);
+ return packages.get(packageName);
+ }
+
+ public void put(int userId, String packageName, ArrayList<String> components) {
+ ArrayMap<String, ArrayList<String>> packages = getOrAllocate(userId);
+ packages.put(packageName, components);
+ }
+
+ public void remove(int userId, String packageName) {
+ ArrayMap<String, ArrayList<String>> packages = mUidMap.get(userId);
+ if (packages != null) {
+ packages.remove(packageName);
+ }
+ }
+
+ public void remove(int userId) {
+ mUidMap.remove(userId);
+ }
+
+ public int userIdCount() {
+ return mUidMap.size();
+ }
+
+ public int userIdAt(int n) {
+ return mUidMap.keyAt(n);
+ }
+
+ public ArrayMap<String, ArrayList<String>> packagesForUserId(int userId) {
+ return mUidMap.get(userId);
+ }
+
+ public int size() {
+ // total number of pending broadcast entries across all userIds
+ int num = 0;
+ for (int i = 0; i < mUidMap.size(); i++) {
+ num += mUidMap.valueAt(i).size();
+ }
+ return num;
+ }
+
+ public void clear() {
+ mUidMap.clear();
+ }
+
+ private ArrayMap<String, ArrayList<String>> getOrAllocate(int userId) {
+ ArrayMap<String, ArrayList<String>> map = mUidMap.get(userId);
+ if (map == null) {
+ map = new ArrayMap<>();
+ mUidMap.put(userId, map);
+ }
+ return map;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PostInstallData.java b/services/core/java/com/android/server/pm/PostInstallData.java
new file mode 100644
index 0000000..65d2a65
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PostInstallData.java
@@ -0,0 +1,40 @@
+/*
+ * 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.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Record-keeping of restore-after-install operations that are currently in flight
+ * between the Package Manager and the Backup Manager
+ */
+public final class PostInstallData {
+ @Nullable
+ public final InstallArgs args;
+ @NonNull
+ public final PackageInstalledInfo res;
+ @Nullable
+ public final Runnable mPostInstallRunnable;
+
+ PostInstallData(@Nullable InstallArgs args, @NonNull PackageInstalledInfo res,
+ @Nullable Runnable postInstallRunnable) {
+ this.args = args;
+ this.res = res;
+ mPostInstallRunnable = postInstallRunnable;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ReconciledPackage.java b/services/core/java/com/android/server/pm/ReconciledPackage.java
index f78249f..bd63a1a 100644
--- a/services/core/java/com/android/server/pm/ReconciledPackage.java
+++ b/services/core/java/com/android/server/pm/ReconciledPackage.java
@@ -82,7 +82,8 @@
combined.putAll(mRequest.mAllPackages);
for (ScanResult scanResult : mRequest.mScannedPackages.values()) {
- combined.put(scanResult.mPkgSetting.name, scanResult.mRequest.mParsedPackage);
+ combined.put(scanResult.mPkgSetting.getPackageName(),
+ scanResult.mRequest.mParsedPackage);
}
return combined;
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
index a76e419..2c54071 100644
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ b/services/core/java/com/android/server/pm/ScanPackageHelper.java
@@ -149,8 +149,8 @@
public void cleanUpAppIdCreation(@NonNull ScanResult result) {
// iff we've acquired an app ID for a new package setting, remove it so that it can be
// acquired by another request.
- if (result.mPkgSetting.appId > 0) {
- mPm.mSettings.removeAppIdLPw(result.mPkgSetting.appId);
+ if (result.mPkgSetting.getAppId() > 0) {
+ mPm.mSettings.removeAppIdLPw(result.mPkgSetting.getAppId());
}
}
@@ -267,7 +267,7 @@
String platformPackageName = mPm.mPlatformPackage == null
? null : mPm.mPlatformPackage.getPackageName();
final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,
- pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,
+ pkgSetting == null ? null : pkgSetting.getPkg(), pkgSetting, disabledPkgSetting,
originalPkgSetting, realPkgName, parseFlags, scanFlags,
Objects.equals(parsedPackage.getPackageName(), platformPackageName), user,
cpuAbiOverride);
@@ -291,7 +291,7 @@
@VisibleForTesting
@NonNull
public ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
- PackageManagerService.Injector injector,
+ PackageManagerServiceInjector injector,
boolean isUnderFactoryTest, long currentTime)
throws PackageManagerException {
final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();
@@ -328,11 +328,11 @@
// API info from existing package setting. However, stub packages currently do not
// preserve ABI info, thus the special condition check here. Remove the special
// check after we fix the stub generation.
- if (pkgSetting.pkg != null && pkgSetting.pkg.isStub()) {
+ if (pkgSetting.getPkg() != null && pkgSetting.getPkg().isStub()) {
needToDeriveAbi = true;
} else {
- primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;
- secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString;
+ primaryCpuAbiFromSettings = pkgSetting.getPrimaryCpuAbi();
+ secondaryCpuAbiFromSettings = pkgSetting.getSecondaryCpuAbi();
}
} else {
// Re-scanning a system package after uninstalling updates; need to derive ABI
@@ -342,18 +342,18 @@
int previousAppId = Process.INVALID_UID;
- if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) {
- if (pkgSetting.sharedUser != null && sharedUserSetting == null) {
- previousAppId = pkgSetting.appId;
+ if (pkgSetting != null && pkgSetting.getSharedUser() != sharedUserSetting) {
+ if (pkgSetting.getSharedUser() != null && sharedUserSetting == null) {
+ previousAppId = pkgSetting.getAppId();
// Log that something is leaving shareduid and keep going
Slog.i(TAG,
"Package " + parsedPackage.getPackageName() + " shared user changed from "
- + pkgSetting.sharedUser.name + " to " + "<nothing>.");
+ + pkgSetting.getSharedUser().name + " to " + "<nothing>.");
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Package " + parsedPackage.getPackageName() + " shared user changed from "
- + (pkgSetting.sharedUser != null
- ? pkgSetting.sharedUser.name : "<nothing>")
+ + (pkgSetting.getSharedUser() != null
+ ? pkgSetting.getSharedUser().name : "<nothing>")
+ " to "
+ (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
+ "; replacing with new");
@@ -397,7 +397,7 @@
} else {
// make a deep copy to avoid modifying any existing system state.
pkgSetting = new PackageSetting(pkgSetting);
- pkgSetting.pkg = parsedPackage;
+ pkgSetting.setPkg(parsedPackage);
// REMOVE SharedUserSetting from method; update in a separate call.
//
@@ -419,11 +419,11 @@
// fix up the new package's name now. We must do this after looking
// up the package under its new name, so getPackageLP takes care of
// fiddling things correctly.
- parsedPackage.setPackageName(originalPkgSetting.name);
+ parsedPackage.setPackageName(originalPkgSetting.getPackageName());
// File a report about this.
- String msg = "New package " + pkgSetting.realName
- + " renamed to replace old package " + pkgSetting.name;
+ String msg = "New package " + pkgSetting.getRealName()
+ + " renamed to replace old package " + pkgSetting.getPackageName();
PackageManagerService.reportSettingsProblem(Log.WARN, msg);
}
@@ -504,8 +504,8 @@
// We haven't run dex-opt for this move (since we've moved the compiled output too)
// but we already have this packages package info in the PackageSetting. We just
// use that and derive the native library path based on the new code path.
- parsedPackage.setPrimaryCpuAbi(pkgSetting.primaryCpuAbiString)
- .setSecondaryCpuAbi(pkgSetting.secondaryCpuAbiString);
+ parsedPackage.setPrimaryCpuAbi(pkgSetting.getPrimaryCpuAbi())
+ .setSecondaryCpuAbi(pkgSetting.getSecondaryCpuAbi());
}
// Set native library paths again. For moves, the path will be updated based on the
@@ -538,9 +538,9 @@
}
}
- pkgSetting.primaryCpuAbiString = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);
- pkgSetting.secondaryCpuAbiString = AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage);
- pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
+ pkgSetting.setPrimaryCpuAbi(AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage))
+ .setSecondaryCpuAbi(AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage))
+ .setCpuAbiOverride(cpuAbiOverride);
if (DEBUG_ABI_SELECTION) {
Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
@@ -551,25 +551,25 @@
// Push the derived path down into PackageSettings so we know what to
// clean up at uninstall time.
- pkgSetting.legacyNativeLibraryPathString = parsedPackage.getNativeLibraryRootDir();
+ pkgSetting.setLegacyNativeLibraryPath(parsedPackage.getNativeLibraryRootDir());
if (DEBUG_ABI_SELECTION) {
Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"
- + " primary=" + pkgSetting.primaryCpuAbiString
- + " secondary=" + pkgSetting.primaryCpuAbiString
- + " abiOverride=" + pkgSetting.cpuAbiOverrideString);
+ + " primary=" + pkgSetting.getPrimaryCpuAbi()
+ + " secondary=" + pkgSetting.getSecondaryCpuAbi()
+ + " abiOverride=" + pkgSetting.getCpuAbiOverride());
}
- if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
+ if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.getSharedUser() != null) {
// We don't do this here during boot because we can do it all
// at once after scanning all existing packages.
//
// We also do this *before* we perform dexopt on this package, so that
// we can avoid redundant dexopts, and also to make sure we've got the
// code and package path correct.
- changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, parsedPackage,
- packageAbiHelper.getAdjustedAbiForSharedUser(
- pkgSetting.sharedUser.packages, parsedPackage));
+ changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.getSharedUser(),
+ parsedPackage, packageAbiHelper.getAdjustedAbiForSharedUser(
+ pkgSetting.getSharedUser().packages, parsedPackage));
}
parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions()
@@ -582,39 +582,41 @@
// Take care of first install / last update times.
final long scanFileTime = getLastModifiedTime(parsedPackage);
if (currentTime != 0) {
- if (pkgSetting.firstInstallTime == 0) {
- pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
+ if (pkgSetting.getFirstInstallTime() == 0) {
+ pkgSetting.setFirstInstallTime(currentTime)
+ .setLastUpdateTime(currentTime);
} else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
- pkgSetting.lastUpdateTime = currentTime;
+ pkgSetting.setLastUpdateTime(currentTime);
}
- } else if (pkgSetting.firstInstallTime == 0) {
+ } else if (pkgSetting.getFirstInstallTime() == 0) {
// We need *something*. Take time time stamp of the file.
- pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
+ pkgSetting.setFirstInstallTime(scanFileTime)
+ .setLastUpdateTime(scanFileTime);
} else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
- if (scanFileTime != pkgSetting.timeStamp) {
+ if (scanFileTime != pkgSetting.getLastModifiedTime()) {
// A package on the system image has changed; consider this
// to be an update.
- pkgSetting.lastUpdateTime = scanFileTime;
+ pkgSetting.setLastUpdateTime(scanFileTime);
}
}
- pkgSetting.setTimeStamp(scanFileTime);
+ pkgSetting.setLastModifiedTime(scanFileTime);
// TODO(b/135203078): Remove, move to constructor
- pkgSetting.pkg = parsedPackage;
+ pkgSetting.setPkg(parsedPackage);
pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);
pkgSetting.pkgPrivateFlags =
PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting);
- if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) {
- pkgSetting.versionCode = parsedPackage.getLongVersionCode();
+ if (parsedPackage.getLongVersionCode() != pkgSetting.getVersionCode()) {
+ pkgSetting.setLongVersionCode(parsedPackage.getLongVersionCode());
}
// Update volume if needed
final String volumeUuid = parsedPackage.getVolumeUuid();
- if (!Objects.equals(volumeUuid, pkgSetting.volumeUuid)) {
+ if (!Objects.equals(volumeUuid, pkgSetting.getVolumeUuid())) {
Slog.i(PackageManagerService.TAG,
"Update" + (pkgSetting.isSystem() ? " system" : "")
+ " package " + parsedPackage.getPackageName()
- + " volume from " + pkgSetting.volumeUuid
+ + " volume from " + pkgSetting.getVolumeUuid()
+ " to " + volumeUuid);
- pkgSetting.volumeUuid = volumeUuid;
+ pkgSetting.setVolumeUuid(volumeUuid);
}
SharedLibraryInfo staticSharedLibraryInfo = null;
@@ -698,7 +700,7 @@
if (pkgSetting.getInstantApp(userId)) {
scanFlags |= SCAN_AS_INSTANT_APP;
}
- if (pkgSetting.getVirtulalPreload(userId)) {
+ if (pkgSetting.getVirtualPreload(userId)) {
scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
}
}
@@ -724,7 +726,7 @@
synchronized (mPm.mLock) {
PackageSetting platformPkgSetting = mPm.mSettings.getPackageLPr("android");
if ((compareSignatures(
- platformPkgSetting.signatures.mSigningDetails.getSignatures(),
+ platformPkgSetting.getSigningDetails().getSignatures(),
pkg.getSigningDetails().getSignatures())
!= PackageManager.SIGNATURE_MATCH)) {
scanFlags |= SCAN_AS_PRIVILEGED;
@@ -779,7 +781,7 @@
pkgSetting = originalPkgSetting == null ? installedPkgSetting : originalPkgSetting;
pkgAlreadyExists = pkgSetting != null;
final String disabledPkgName = pkgAlreadyExists
- ? pkgSetting.name : parsedPackage.getPackageName();
+ ? pkgSetting.getPackageName() : parsedPackage.getPackageName();
if (scanSystemPartition && !pkgAlreadyExists
&& mPm.mSettings.getDisabledSystemPkgLPr(disabledPkgName) != null) {
// The updated-package data for /system apk remains inconsistently
@@ -834,8 +836,8 @@
final boolean newPkgChangedPaths = pkgAlreadyExists
&& !pkgSetting.getPathString().equals(parsedPackage.getPath());
- final boolean newPkgVersionGreater =
- pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
+ final boolean newPkgVersionGreater = pkgAlreadyExists
+ && parsedPackage.getLongVersionCode() > pkgSetting.getVersionCode();
final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
&& newPkgChangedPaths && newPkgVersionGreater;
if (isSystemPkgBetter) {
@@ -845,23 +847,23 @@
// there won't be a working copy of the application.
synchronized (mPm.mLock) {
// just remove the loaded entries from package lists
- mPm.mPackages.remove(pkgSetting.name);
+ mPm.mPackages.remove(pkgSetting.getPackageName());
}
logCriticalInfo(Log.WARN,
"System package updated;"
- + " name: " + pkgSetting.name
- + "; " + pkgSetting.versionCode + " --> "
+ + " name: " + pkgSetting.getPackageName()
+ + "; " + pkgSetting.getVersionCode() + " --> "
+ parsedPackage.getLongVersionCode()
+ "; " + pkgSetting.getPathString()
+ " --> " + parsedPackage.getPath());
final InstallArgs args = mPm.createInstallArgsForExisting(
pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
+ pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()));
args.cleanUpResourcesLI();
synchronized (mPm.mLock) {
- mPm.mSettings.enableSystemPackageLPw(pkgSetting.name);
+ mPm.mSettings.enableSystemPackageLPw(pkgSetting.getPackageName());
}
}
@@ -877,7 +879,7 @@
parsedPackage.hideAsFinal();
throw new PackageManagerException(Log.WARN, "Package " + parsedPackage.getPackageName()
+ " at " + parsedPackage.getPath() + " ignored: updated version "
- + (pkgAlreadyExists ? String.valueOf(pkgSetting.versionCode) : "unknown")
+ + (pkgAlreadyExists ? String.valueOf(pkgSetting.getVersionCode()) : "unknown")
+ " better than this " + parsedPackage.getLongVersionCode());
}
@@ -915,14 +917,14 @@
&& !pkgSetting.isSystem()) {
if (!parsedPackage.getSigningDetails()
- .checkCapability(pkgSetting.signatures.mSigningDetails,
+ .checkCapability(pkgSetting.getSigningDetails(),
SigningDetails.CertCapabilities.INSTALLED_DATA)
- && !pkgSetting.signatures.mSigningDetails.checkCapability(
+ && !pkgSetting.getSigningDetails().checkCapability(
parsedPackage.getSigningDetails(),
SigningDetails.CertCapabilities.ROLLBACK)) {
logCriticalInfo(Log.WARN,
"System package signature mismatch;"
- + " name: " + pkgSetting.name);
+ + " name: " + pkgSetting.getPackageName());
try (@SuppressWarnings("unused") PackageFreezer freezer = mPm.freezePackage(
parsedPackage.getPackageName(),
"scanPackageInternalLI")) {
@@ -936,14 +938,14 @@
// and replace it with the version on /system.
logCriticalInfo(Log.WARN,
"System package enabled;"
- + " name: " + pkgSetting.name
- + "; " + pkgSetting.versionCode + " --> "
+ + " name: " + pkgSetting.getPackageName()
+ + "; " + pkgSetting.getVersionCode() + " --> "
+ parsedPackage.getLongVersionCode()
+ "; " + pkgSetting.getPathString() + " --> "
+ parsedPackage.getPath());
InstallArgs args = mPm.createInstallArgsForExisting(
pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
+ pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()));
synchronized (mPm.mInstallLock) {
args.cleanUpResourcesLI();
}
@@ -954,9 +956,9 @@
shouldHideSystemApp = true;
logCriticalInfo(Log.INFO,
"System package disabled;"
- + " name: " + pkgSetting.name
+ + " name: " + pkgSetting.getPackageName()
+ "; old: " + pkgSetting.getPathString() + " @ "
- + pkgSetting.versionCode
+ + pkgSetting.getVersionCode()
+ "; new: " + parsedPackage.getPath() + " @ "
+ parsedPackage.getPath());
}
@@ -968,7 +970,7 @@
synchronized (mPm.mLock) {
boolean appIdCreated = false;
try {
- final String pkgName = scanResult.mPkgSetting.name;
+ final String pkgName = scanResult.mPkgSetting.getPackageName();
final Map<String, ReconciledPackage> reconcileResult =
mPm.reconcilePackagesLocked(
new ReconcileRequest(
@@ -1010,7 +1012,7 @@
new IncrementalProgressListener(parsedPackage.getPackageName(), mPm));
}
}
- return scanResult.mPkgSetting.pkg;
+ return scanResult.mPkgSetting.getPkg();
}
/**
@@ -1071,22 +1073,22 @@
mPm.getSettingsVersionForPackage(parsedPackage);
if (ps != null && !forceCollect
&& ps.getPathString().equals(parsedPackage.getPath())
- && ps.timeStamp == lastModifiedTime
+ && ps.getLastModifiedTime() == lastModifiedTime
&& !PackageManagerService.isCompatSignatureUpdateNeeded(settingsVersionForPackage)
&& !PackageManagerService.isRecoverSignatureUpdateNeeded(
settingsVersionForPackage)) {
- if (ps.signatures.mSigningDetails.getSignatures() != null
- && ps.signatures.mSigningDetails.getSignatures().length != 0
- && ps.signatures.mSigningDetails.getSignatureSchemeVersion()
+ if (ps.getSigningDetails().getSignatures() != null
+ && ps.getSigningDetails().getSignatures().length != 0
+ && ps.getSigningDetails().getSignatureSchemeVersion()
!= SigningDetails.SignatureSchemeVersion.UNKNOWN) {
// Optimization: reuse the existing cached signing data
// if the package appears to be unchanged.
parsedPackage.setSigningDetails(
- new SigningDetails(ps.signatures.mSigningDetails));
+ new SigningDetails(ps.getSigningDetails()));
return;
}
- Slog.w(TAG, "PackageSetting for " + ps.name
+ Slog.w(TAG, "PackageSetting for " + ps.getPackageName()
+ " is missing signatures. Collecting certs again to recover them.");
} else {
Slog.i(TAG, parsedPackage.getPath() + " changed; collecting certs"
@@ -1118,15 +1120,15 @@
if (originalPkgSetting == null || !mPm.isDeviceUpgrading()) {
return;
}
- if (originalPkgSetting.versionCode == pkg.getLongVersionCode()) {
+ if (originalPkgSetting.getVersionCode() == pkg.getLongVersionCode()) {
return;
}
mPm.clearAppProfilesLIF(pkg);
if (DEBUG_INSTALL) {
- Slog.d(TAG, originalPkgSetting.name
+ Slog.d(TAG, originalPkgSetting.getPackageName()
+ " clear profile due to version change "
- + originalPkgSetting.versionCode + " != "
+ + originalPkgSetting.getVersionCode() + " != "
+ pkg.getLongVersionCode());
}
}
@@ -1154,12 +1156,12 @@
if (!verifyPackageUpdateLPr(originalPs, pkg)) {
// the new package is incompatible with the original
continue;
- } else if (originalPs.sharedUser != null) {
- if (!originalPs.sharedUser.name.equals(pkg.getSharedUserId())) {
+ } else if (originalPs.getSharedUser() != null) {
+ if (!originalPs.getSharedUser().name.equals(pkg.getSharedUserId())) {
// the shared user id is incompatible with the original
- Slog.w(TAG, "Unable to migrate data from " + originalPs.name
+ Slog.w(TAG, "Unable to migrate data from " + originalPs.getPackageName()
+ " to " + pkg.getPackageName() + ": old uid "
- + originalPs.sharedUser.name
+ + originalPs.getSharedUser().name
+ " differs from " + pkg.getSharedUserId());
continue;
}
@@ -1167,7 +1169,8 @@
} else {
if (DEBUG_UPGRADE) {
Log.v(TAG, "Renaming new package "
- + pkg.getPackageName() + " to old name " + originalPs.name);
+ + pkg.getPackageName() + " to old name "
+ + originalPs.getPackageName());
}
}
return originalPs;
@@ -1179,12 +1182,12 @@
@GuardedBy("mPm.mLock")
private boolean verifyPackageUpdateLPr(PackageSetting oldPkg, AndroidPackage newPkg) {
if ((oldPkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- Slog.w(TAG, "Unable to update from " + oldPkg.name
+ Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
+ " to " + newPkg.getPackageName()
+ ": old package not in system partition");
return false;
- } else if (mPm.mPackages.get(oldPkg.name) != null) {
- Slog.w(TAG, "Unable to update from " + oldPkg.name
+ } else if (mPm.mPackages.get(oldPkg.getPackageName()) != null) {
+ Slog.w(TAG, "Unable to update from " + oldPkg.getPackageName()
+ " to " + newPkg.getPackageName()
+ ": old package still exists");
return false;
@@ -1592,19 +1595,20 @@
}
List<String> changedAbiCodePath = null;
for (PackageSetting ps : sharedUserSetting.packages) {
- if (scannedPackage == null || !scannedPackage.getPackageName().equals(ps.name)) {
- if (ps.primaryCpuAbiString != null) {
+ if (scannedPackage == null
+ || !scannedPackage.getPackageName().equals(ps.getPackageName())) {
+ if (ps.getPrimaryCpuAbi() != null) {
continue;
}
- ps.primaryCpuAbiString = adjustedAbi;
- if (ps.pkg != null) {
+ ps.setPrimaryCpuAbi(adjustedAbi);
+ if (ps.getPkg() != null) {
if (!TextUtils.equals(adjustedAbi,
- AndroidPackageUtils.getRawPrimaryCpuAbi(ps.pkg))) {
+ AndroidPackageUtils.getRawPrimaryCpuAbi(ps.getPkg()))) {
if (DEBUG_ABI_SELECTION) {
Slog.i(TAG,
- "Adjusting ABI for " + ps.name + " to " + adjustedAbi
- + " (scannedPackage="
+ "Adjusting ABI for " + ps.getPackageName() + " to "
+ + adjustedAbi + " (scannedPackage="
+ (scannedPackage != null ? scannedPackage : "null")
+ ")");
}
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 7b5c7e3..22e6ece 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -101,24 +101,20 @@
* purposes other than migration.
*/
@Deprecated
- protected final LegacyPermissionState mLegacyPermissionsState;
+ protected final LegacyPermissionState mLegacyPermissionsState = new LegacyPermissionState();
SettingBase(int pkgFlags, int pkgPrivateFlags) {
setFlags(pkgFlags);
setPrivateFlags(pkgPrivateFlags);
- mLegacyPermissionsState = new LegacyPermissionState();
}
- SettingBase(SettingBase orig) {
- mLegacyPermissionsState = new LegacyPermissionState();
- doCopy(orig);
+ SettingBase(@Nullable SettingBase orig) {
+ if (orig != null) {
+ copySettingBase(orig);
+ }
}
- public void copyFrom(SettingBase orig) {
- doCopy(orig);
- }
-
- private void doCopy(SettingBase orig) {
+ public final void copySettingBase(SettingBase orig) {
pkgFlags = orig.pkgFlags;
pkgPrivateFlags = orig.pkgPrivateFlags;
mLegacyPermissionsState.copyFrom(orig.mLegacyPermissionsState);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f111967..8ee2588 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -786,7 +786,7 @@
}
final PackageSetting dp = mDisabledSysPackages.get(name);
// always make sure the system package code and resource paths dont change
- if (dp == null && p.pkg != null && p.pkg.isSystem()
+ if (dp == null && p.getPkg() != null && p.getPkg().isSystem()
&& !p.getPkgState().isUpdatedSystemApp()) {
p.getPkgState().setUpdatedSystemApp(true);
final PackageSetting disabled;
@@ -813,10 +813,10 @@
return null;
}
p.getPkgState().setUpdatedSystemApp(false);
- PackageSetting ret = addPackageLPw(name, p.realName, p.getPath(),
- p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
- p.secondaryCpuAbiString, p.cpuAbiOverrideString,
- p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
+ PackageSetting ret = addPackageLPw(name, p.getRealName(), p.getPath(),
+ p.getLegacyNativeLibraryPath(), p.getPrimaryCpuAbi(),
+ p.getSecondaryCpuAbi(), p.getCpuAbiOverride(),
+ p.getAppId(), p.getLongVersionCode(), p.pkgFlags, p.pkgPrivateFlags,
p.usesStaticLibraries, p.usesStaticLibrariesVersions, p.mimeGroups,
mDomainVerificationManager.generateNewId());
if (ret != null) {
@@ -842,7 +842,7 @@
@NonNull UUID domainSetId) {
PackageSetting p = mPackages.get(name);
if (p != null) {
- if (p.appId == uid) {
+ if (p.getAppId() == uid) {
return p;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
@@ -853,7 +853,7 @@
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags,
pkgPrivateFlags, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames,
mimeGroups, domainSetId);
- p.appId = uid;
+ p.setAppId(uid);
if (registerExistingAppIdLPw(uid, p, name)) {
mPackages.put(name, p);
return p;
@@ -891,7 +891,7 @@
// remove packages that are no longer installed
for (Iterator<PackageSetting> iter = sus.packages.iterator(); iter.hasNext();) {
PackageSetting ps = iter.next();
- if (mPackages.get(ps.name) == null) {
+ if (mPackages.get(ps.getPackageName()) == null) {
iter.remove();
}
}
@@ -920,22 +920,22 @@
final PackageSetting pkgSetting;
if (originalPkg != null) {
if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
- + pkgName + " is adopting original package " + originalPkg.name);
+ + pkgName + " is adopting original package " + originalPkg.getPackageName());
pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/);
pkgSetting.setPath(codePath);
- pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
+ pkgSetting.setLegacyNativeLibraryPath(legacyNativeLibraryPath);
pkgSetting.pkgFlags = pkgFlags;
pkgSetting.pkgPrivateFlags = pkgPrivateFlags;
- pkgSetting.primaryCpuAbiString = primaryCpuAbi;
- pkgSetting.secondaryCpuAbiString = secondaryCpuAbi;
+ pkgSetting.setPrimaryCpuAbi(primaryCpuAbi);
+ pkgSetting.setSecondaryCpuAbi(secondaryCpuAbi);
// NOTE: Create a deeper copy of the package signatures so we don't
// overwrite the signatures in the original package setting.
- pkgSetting.signatures = new PackageSignatures();
- pkgSetting.versionCode = versionCode;
+ pkgSetting.setSignatures(new PackageSignatures());
+ pkgSetting.setLongVersionCode(versionCode);
pkgSetting.usesStaticLibraries = usesStaticLibraries;
pkgSetting.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
// Update new package state.
- pkgSetting.setTimeStamp(codePath.lastModified());
+ pkgSetting.setLastModifiedTime(codePath.lastModified());
pkgSetting.setDomainSetId(domainSetId);
} else {
pkgSetting = new PackageSetting(pkgName, realPkgName, codePath,
@@ -943,8 +943,8 @@
null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
0 /*sharedUserId*/, usesStaticLibraries,
usesStaticLibrariesVersions, createMimeGroups(mimeGroupNames), domainSetId);
- pkgSetting.setTimeStamp(codePath.lastModified());
- pkgSetting.sharedUser = sharedUser;
+ pkgSetting.setLastModifiedTime(codePath.lastModified());
+ pkgSetting.setSharedUser(sharedUser);
// If this is not a system app, it starts out stopped.
if ((pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
if (DEBUG_STOPPED) {
@@ -988,7 +988,7 @@
}
}
if (sharedUser != null) {
- pkgSetting.appId = sharedUser.userId;
+ pkgSetting.setAppId(sharedUser.userId);
} else {
// Clone the setting here for disabled system packages
if (disabledPkg != null) {
@@ -996,8 +996,8 @@
// from the existing user id. This still has to be
// added to list of user id's
// Copy signatures from previous setting
- pkgSetting.signatures = new PackageSignatures(disabledPkg.signatures);
- pkgSetting.appId = disabledPkg.appId;
+ pkgSetting.setSignatures(new PackageSignatures(disabledPkg.getSignatures()));
+ pkgSetting.setAppId(disabledPkg.getAppId());
// Clone permissions
pkgSetting.getLegacyPermissionState()
.copyFrom(disabledPkg.getLegacyPermissionState());
@@ -1039,16 +1039,17 @@
@Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions,
@Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId)
throws PackageManagerException {
- final String pkgName = pkgSetting.name;
- if (!Objects.equals(pkgSetting.sharedUser, sharedUser) && sharedUser != null) {
+ final String pkgName = pkgSetting.getPackageName();
+ if (!Objects.equals(pkgSetting.getSharedUser(), sharedUser) && sharedUser != null) {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Package " + pkgName + " shared user changed from "
- + (pkgSetting.sharedUser != null ? pkgSetting.sharedUser.name : "<nothing>")
+ + (pkgSetting.getSharedUser() != null
+ ? pkgSetting.getSharedUser().name : "<nothing>")
+ " to " + sharedUser.name);
throw new PackageManagerException(INSTALL_FAILED_UID_CHANGED,
"Updating application package " + pkgName + " failed");
}
- pkgSetting.sharedUser = sharedUser;
+ pkgSetting.setSharedUser(sharedUser);
if (!pkgSetting.getPath().equals(codePath)) {
final boolean isSystem = pkgSetting.isSystem();
@@ -1075,11 +1076,11 @@
// Since we've changed paths, prefer the new native library path over
// the one stored in the package settings since we might have moved from
// internal to external storage or vice versa.
- pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
+ pkgSetting.setLegacyNativeLibraryPath(legacyNativeLibraryPath);
}
pkgSetting.setPath(codePath);
if (IncrementalManager.isIncrementalPath(codePath.getAbsolutePath())) {
- pkgSetting.incrementalStates = new IncrementalStates();
+ pkgSetting.setIncrementalStates(new IncrementalStates());
}
}
// If what we are scanning is a system (and possibly privileged) package,
@@ -1105,8 +1106,8 @@
pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT;
pkgSetting.pkgPrivateFlags |=
pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_ODM;
- pkgSetting.primaryCpuAbiString = primaryCpuAbi;
- pkgSetting.secondaryCpuAbiString = secondaryCpuAbi;
+ pkgSetting.setPrimaryCpuAbi(primaryCpuAbi);
+ pkgSetting.setSecondaryCpuAbi(secondaryCpuAbi);
// Update static shared library dependencies if needed
if (usesStaticLibraries != null && usesStaticLibrariesVersions != null
&& usesStaticLibraries.length == usesStaticLibrariesVersions.length) {
@@ -1129,19 +1130,19 @@
*/
boolean registerAppIdLPw(PackageSetting p, boolean forceNew) throws PackageManagerException {
final boolean createdNew;
- if (p.appId == 0 || forceNew) {
+ if (p.getAppId() == 0 || forceNew) {
// Assign new user ID
- p.appId = acquireAndRegisterNewAppIdLPw(p);
+ p.setAppId(acquireAndRegisterNewAppIdLPw(p));
createdNew = true;
} else {
// Add new setting to list of user IDs
- createdNew = registerExistingAppIdLPw(p.appId, p, p.name);
+ createdNew = registerExistingAppIdLPw(p.getAppId(), p, p.getPackageName());
}
- if (p.appId < 0) {
+ if (p.getAppId() < 0) {
PackageManagerService.reportSettingsProblem(Log.WARN,
- "Package " + p.name + " could not be assigned a valid UID");
+ "Package " + p.getPackageName() + " could not be assigned a valid UID");
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
- "Package " + p.name + " could not be assigned a valid UID");
+ "Package " + p.getPackageName() + " could not be assigned a valid UID");
}
return createdNew;
}
@@ -1152,7 +1153,7 @@
*/
void writeUserRestrictionsLPw(PackageSetting newPackage, PackageSetting oldPackage) {
// package doesn't exist; do nothing
- if (getPackageLPr(newPackage.name) == null) {
+ if (getPackageLPr(newPackage.getPackageName()) == null) {
return;
}
// no users defined; do nothing
@@ -1179,53 +1180,53 @@
// by that time.
void insertPackageSettingLPw(PackageSetting p, AndroidPackage pkg) {
// Update signatures if needed.
- if (p.signatures.mSigningDetails.getSignatures() == null) {
- p.signatures.mSigningDetails = pkg.getSigningDetails();
+ if (p.getSigningDetails().getSignatures() == null) {
+ p.setSigningDetails(pkg.getSigningDetails());
}
// If this app defines a shared user id initialize
// the shared user signatures as well.
- if (p.sharedUser != null
- && p.sharedUser.signatures.mSigningDetails.getSignatures() == null) {
- p.sharedUser.signatures.mSigningDetails = pkg.getSigningDetails();
+ if (p.getSharedUser() != null
+ && p.getSharedUser().signatures.mSigningDetails.getSignatures() == null) {
+ p.getSharedUser().signatures.mSigningDetails = pkg.getSigningDetails();
}
- addPackageSettingLPw(p, p.sharedUser);
+ addPackageSettingLPw(p, p.getSharedUser());
}
// Utility method that adds a PackageSetting to mPackages and
// completes updating the shared user attributes and any restored
// app link verification state
private void addPackageSettingLPw(PackageSetting p, SharedUserSetting sharedUser) {
- mPackages.put(p.name, p);
+ mPackages.put(p.getPackageName(), p);
if (sharedUser != null) {
- if (p.sharedUser != null && p.sharedUser != sharedUser) {
+ if (p.getSharedUser() != null && p.getSharedUser() != sharedUser) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Package " + p.name + " was user "
- + p.sharedUser + " but is now " + sharedUser
+ "Package " + p.getPackageName() + " was user "
+ + p.getSharedUser() + " but is now " + sharedUser
+ "; I am not changing its files so it will probably fail!");
- p.sharedUser.removePackage(p);
- } else if (p.appId != sharedUser.userId) {
+ p.getSharedUser().removePackage(p);
+ } else if (p.getAppId() != sharedUser.userId) {
PackageManagerService.reportSettingsProblem(Log.ERROR,
- "Package " + p.name + " was user id " + p.appId
+ "Package " + p.getPackageName() + " was user id " + p.getAppId()
+ " but is now user " + sharedUser
+ " with id " + sharedUser.userId
+ "; I am not changing its files so it will probably fail!");
}
sharedUser.addPackage(p);
- p.sharedUser = sharedUser;
- p.appId = sharedUser.userId;
+ p.setSharedUser(sharedUser);
+ p.setAppId(sharedUser.userId);
}
// If the we know about this user id, we have to update it as it
// has to point to the same PackageSetting instance as the package.
- Object userIdPs = getSettingLPr(p.appId);
+ Object userIdPs = getSettingLPr(p.getAppId());
if (sharedUser == null) {
if (userIdPs != null && userIdPs != p) {
- replaceAppIdLPw(p.appId, p);
+ replaceAppIdLPw(p.getAppId(), p);
}
} else {
if (userIdPs != null && userIdPs != sharedUser) {
- replaceAppIdLPw(p.appId, sharedUser);
+ replaceAppIdLPw(p.getAppId(), sharedUser);
}
}
}
@@ -1235,16 +1236,16 @@
if (p != null) {
mPackages.remove(name);
removeInstallerPackageStatus(name);
- if (p.sharedUser != null) {
- p.sharedUser.removePackage(p);
- if (p.sharedUser.packages.size() == 0) {
- mSharedUsers.remove(p.sharedUser.name);
- removeAppIdLPw(p.sharedUser.userId);
- return p.sharedUser.userId;
+ if (p.getSharedUser() != null) {
+ p.getSharedUser().removePackage(p);
+ if (p.getSharedUser().packages.size() == 0) {
+ mSharedUsers.remove(p.getSharedUser().name);
+ removeAppIdLPw(p.getSharedUser().userId);
+ return p.getSharedUser().userId;
}
} else {
- removeAppIdLPw(p.appId);
- return p.appId;
+ removeAppIdLPw(p.getAppId());
+ return p.getAppId();
}
}
return -1;
@@ -1995,12 +1996,12 @@
for (final PackageSetting pkg : mPackages.values()) {
final PackageUserState ustate = pkg.readUserState(userId);
if (DEBUG_MU) {
- Log.i(TAG, " pkg=" + pkg.name + ", installed=" + ustate.installed
+ Log.i(TAG, " pkg=" + pkg.getPackageName() + ", installed=" + ustate.installed
+ ", state=" + ustate.enabled);
}
serializer.startTag(null, TAG_PACKAGE);
- serializer.attribute(null, ATTR_NAME, pkg.name);
+ serializer.attribute(null, ATTR_NAME, pkg.getPackageName());
if (ustate.ceDataInode != 0) {
serializer.attributeLong(null, ATTR_CE_DATA_INODE, ustate.ceDataInode);
}
@@ -2445,7 +2446,7 @@
for (final PackageSetting ps : mPackages.values()) {
// Package is actively claimed
- knownSet.remove(ps.name);
+ knownSet.remove(ps.getPackageName());
writeKernelMappingLPr(ps);
}
@@ -2460,9 +2461,9 @@
}
void writeKernelMappingLPr(PackageSetting ps) {
- if (mKernelMappingFilename == null || ps == null || ps.name == null) return;
+ if (mKernelMappingFilename == null || ps == null || ps.getPackageName() == null) return;
- writeKernelMappingLPr(ps.name, ps.appId, ps.getNotInstalledUserIds());
+ writeKernelMappingLPr(ps.getPackageName(), ps.getAppId(), ps.getNotInstalledUserIds());
}
void writeKernelMappingLPr(String name, int appId, int[] excludedUserIds) {
@@ -2571,22 +2572,22 @@
StringBuilder sb = new StringBuilder();
for (final PackageSetting pkg : mPackages.values()) {
// TODO(b/135203078): This doesn't handle multiple users
- final String dataPath = pkg.pkg == null ? null :
- PackageInfoWithoutStateUtils.getDataDir(pkg.pkg,
+ final String dataPath = pkg.getPkg() == null ? null :
+ PackageInfoWithoutStateUtils.getDataDir(pkg.getPkg(),
UserHandle.USER_SYSTEM).getAbsolutePath();
- if (pkg.pkg == null || dataPath == null) {
- if (!"android".equals(pkg.name)) {
+ if (pkg.getPkg() == null || dataPath == null) {
+ if (!"android".equals(pkg.getPackageName())) {
Slog.w(TAG, "Skipping " + pkg + " due to missing metadata");
}
continue;
}
- final boolean isDebug = pkg.pkg.isDebuggable();
+ final boolean isDebug = pkg.getPkg().isDebuggable();
final IntArray gids = new IntArray();
for (final int userId : userIds) {
gids.addAll(mPermissionDataProvider.getGidsForUid(UserHandle.getUid(userId,
- pkg.appId)));
+ pkg.getAppId())));
}
// Avoid any application that has a space in its path.
@@ -2614,13 +2615,13 @@
// system/core/libpackagelistparser
//
sb.setLength(0);
- sb.append(pkg.pkg.getPackageName());
+ sb.append(pkg.getPkg().getPackageName());
sb.append(" ");
- sb.append(pkg.pkg.getUid());
+ sb.append(pkg.getPkg().getUid());
sb.append(isDebug ? " 1 " : " 0 ");
sb.append(dataPath);
sb.append(" ");
- sb.append(AndroidPackageUtils.getSeInfo(pkg.pkg, pkg));
+ sb.append(AndroidPackageUtils.getSeInfo(pkg.getPkg(), pkg));
sb.append(" ");
final int gidsSize = gids.size();
if (gids != null && gids.size() > 0) {
@@ -2633,19 +2634,19 @@
sb.append("none");
}
sb.append(" ");
- sb.append(pkg.pkg.isProfileableByShell() ? "1" : "0");
+ sb.append(pkg.getPkg().isProfileableByShell() ? "1" : "0");
sb.append(" ");
- sb.append(pkg.pkg.getLongVersionCode());
+ sb.append(pkg.getPkg().getLongVersionCode());
sb.append(" ");
- sb.append(pkg.pkg.isProfileable() ? "1" : "0");
+ sb.append(pkg.getPkg().isProfileable() ? "1" : "0");
sb.append(" ");
if (pkg.isSystem()) {
sb.append("@system");
} else if (pkg.isProduct()) {
sb.append("@product");
- } else if (pkg.installSource.installerPackageName != null
- && !pkg.installSource.installerPackageName.isEmpty()) {
- sb.append(pkg.installSource.installerPackageName);
+ } else if (pkg.getInstallSource().installerPackageName != null
+ && !pkg.getInstallSource().installerPackageName.isEmpty()) {
+ sb.append(pkg.getInstallSource().installerPackageName);
} else {
sb.append("@null");
}
@@ -2666,35 +2667,35 @@
void writeDisabledSysPackageLPr(TypedXmlSerializer serializer, final PackageSetting pkg)
throws java.io.IOException {
serializer.startTag(null, "updated-package");
- serializer.attribute(null, ATTR_NAME, pkg.name);
- if (pkg.realName != null) {
- serializer.attribute(null, "realName", pkg.realName);
+ serializer.attribute(null, ATTR_NAME, pkg.getPackageName());
+ if (pkg.getRealName() != null) {
+ serializer.attribute(null, "realName", pkg.getRealName());
}
serializer.attribute(null, "codePath", pkg.getPathString());
- serializer.attributeLongHex(null, "ft", pkg.timeStamp);
- serializer.attributeLongHex(null, "it", pkg.firstInstallTime);
- serializer.attributeLongHex(null, "ut", pkg.lastUpdateTime);
- serializer.attributeLong(null, "version", pkg.versionCode);
- if (pkg.legacyNativeLibraryPathString != null) {
- serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString);
+ serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime());
+ serializer.attributeLongHex(null, "it", pkg.getFirstInstallTime());
+ serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime());
+ serializer.attributeLong(null, "version", pkg.getLongVersionCode());
+ if (pkg.getLegacyNativeLibraryPath() != null) {
+ serializer.attribute(null, "nativeLibraryPath", pkg.getLegacyNativeLibraryPath());
}
- if (pkg.primaryCpuAbiString != null) {
- serializer.attribute(null, "primaryCpuAbi", pkg.primaryCpuAbiString);
+ if (pkg.getPrimaryCpuAbi() != null) {
+ serializer.attribute(null, "primaryCpuAbi", pkg.getPrimaryCpuAbi());
}
- if (pkg.secondaryCpuAbiString != null) {
- serializer.attribute(null, "secondaryCpuAbi", pkg.secondaryCpuAbiString);
+ if (pkg.getSecondaryCpuAbi() != null) {
+ serializer.attribute(null, "secondaryCpuAbi", pkg.getSecondaryCpuAbi());
}
- if (pkg.cpuAbiOverrideString != null) {
- serializer.attribute(null, "cpuAbiOverride", pkg.cpuAbiOverrideString);
+ if (pkg.getCpuAbiOverride() != null) {
+ serializer.attribute(null, "cpuAbiOverride", pkg.getCpuAbiOverride());
}
- if (pkg.sharedUser == null) {
- serializer.attributeInt(null, "userId", pkg.appId);
+ if (pkg.getSharedUser() == null) {
+ serializer.attributeInt(null, "userId", pkg.getAppId());
} else {
- serializer.attributeInt(null, "sharedUserId", pkg.appId);
+ serializer.attributeInt(null, "sharedUserId", pkg.getAppId());
}
serializer.attributeFloat(null, "loadingProgress",
- pkg.getIncrementalStates().getProgress());
+ pkg.getIncrementalStates().getIncrementalStatesInfo().getProgress());
writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
@@ -2704,37 +2705,37 @@
void writePackageLPr(TypedXmlSerializer serializer, final PackageSetting pkg)
throws java.io.IOException {
serializer.startTag(null, "package");
- serializer.attribute(null, ATTR_NAME, pkg.name);
- if (pkg.realName != null) {
- serializer.attribute(null, "realName", pkg.realName);
+ serializer.attribute(null, ATTR_NAME, pkg.getPackageName());
+ if (pkg.getRealName() != null) {
+ serializer.attribute(null, "realName", pkg.getRealName());
}
serializer.attribute(null, "codePath", pkg.getPathString());
- if (pkg.legacyNativeLibraryPathString != null) {
- serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString);
+ if (pkg.getLegacyNativeLibraryPath() != null) {
+ serializer.attribute(null, "nativeLibraryPath", pkg.getLegacyNativeLibraryPath());
}
- if (pkg.primaryCpuAbiString != null) {
- serializer.attribute(null, "primaryCpuAbi", pkg.primaryCpuAbiString);
+ if (pkg.getPrimaryCpuAbi() != null) {
+ serializer.attribute(null, "primaryCpuAbi", pkg.getPrimaryCpuAbi());
}
- if (pkg.secondaryCpuAbiString != null) {
- serializer.attribute(null, "secondaryCpuAbi", pkg.secondaryCpuAbiString);
+ if (pkg.getSecondaryCpuAbi() != null) {
+ serializer.attribute(null, "secondaryCpuAbi", pkg.getSecondaryCpuAbi());
}
- if (pkg.cpuAbiOverrideString != null) {
- serializer.attribute(null, "cpuAbiOverride", pkg.cpuAbiOverrideString);
+ if (pkg.getCpuAbiOverride() != null) {
+ serializer.attribute(null, "cpuAbiOverride", pkg.getCpuAbiOverride());
}
serializer.attributeInt(null, "publicFlags", pkg.pkgFlags);
serializer.attributeInt(null, "privateFlags", pkg.pkgPrivateFlags);
- serializer.attributeLongHex(null, "ft", pkg.timeStamp);
- serializer.attributeLongHex(null, "it", pkg.firstInstallTime);
- serializer.attributeLongHex(null, "ut", pkg.lastUpdateTime);
- serializer.attributeLong(null, "version", pkg.versionCode);
- if (pkg.sharedUser == null) {
- serializer.attributeInt(null, "userId", pkg.appId);
+ serializer.attributeLongHex(null, "ft", pkg.getLastModifiedTime());
+ serializer.attributeLongHex(null, "it", pkg.getFirstInstallTime());
+ serializer.attributeLongHex(null, "ut", pkg.getLastUpdateTime());
+ serializer.attributeLong(null, "version", pkg.getLongVersionCode());
+ if (pkg.getSharedUser() == null) {
+ serializer.attributeInt(null, "userId", pkg.getAppId());
} else {
- serializer.attributeInt(null, "sharedUserId", pkg.appId);
+ serializer.attributeInt(null, "sharedUserId", pkg.getAppId());
}
- InstallSource installSource = pkg.installSource;
+ InstallSource installSource = pkg.getInstallSource();
if (installSource.installerPackageName != null) {
serializer.attribute(null, "installer", installSource.installerPackageName);
}
@@ -2754,38 +2755,38 @@
if (installSource.originatingPackageName != null) {
serializer.attribute(null, "installOriginator", installSource.originatingPackageName);
}
- if (pkg.volumeUuid != null) {
- serializer.attribute(null, "volumeUuid", pkg.volumeUuid);
+ if (pkg.getVolumeUuid() != null) {
+ serializer.attribute(null, "volumeUuid", pkg.getVolumeUuid());
}
- if (pkg.categoryHint != ApplicationInfo.CATEGORY_UNDEFINED) {
- serializer.attributeInt(null, "categoryHint", pkg.categoryHint);
+ if (pkg.getCategoryOverride() != ApplicationInfo.CATEGORY_UNDEFINED) {
+ serializer.attributeInt(null, "categoryHint", pkg.getCategoryOverride());
}
- if (pkg.updateAvailable) {
+ if (pkg.isUpdateAvailable()) {
serializer.attributeBoolean(null, "updateAvailable", true);
}
- if (pkg.forceQueryableOverride) {
+ if (pkg.isForceQueryableOverride()) {
serializer.attributeBoolean(null, "forceQueryable", true);
}
if (pkg.isPackageLoading()) {
serializer.attributeBoolean(null, "isLoading", true);
}
serializer.attributeFloat(null, "loadingProgress",
- pkg.getIncrementalStates().getProgress());
+ pkg.getIncrementalStates().getIncrementalStatesInfo().getProgress());
serializer.attribute(null, "domainSetId", pkg.getDomainSetId().toString());
writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
- pkg.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage());
+ pkg.getSignatures().writeXml(serializer, "sigs", mPastSignatures.untrackedStorage());
if (installSource.initiatingPackageSignatures != null) {
installSource.initiatingPackageSignatures.writeXml(
serializer, "install-initiator-sigs", mPastSignatures.untrackedStorage());
}
- writeSigningKeySetLPr(serializer, pkg.keySetData);
- writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
- writeKeySetAliasesLPr(serializer, pkg.keySetData);
+ writeSigningKeySetLPr(serializer, pkg.getKeySetData());
+ writeUpgradeKeySetsLPr(serializer, pkg.getKeySetData());
+ writeKeySetAliasesLPr(serializer, pkg.getKeySetData());
writeMimeGroupLPr(serializer, pkg.mimeGroups);
serializer.endTag(null, "package");
@@ -3006,17 +3007,17 @@
final Object idObj = getSettingLPr(sharedUserId);
if (idObj instanceof SharedUserSetting) {
final SharedUserSetting sharedUser = (SharedUserSetting) idObj;
- p.sharedUser = sharedUser;
- p.appId = sharedUser.userId;
+ p.setSharedUser(sharedUser);
+ p.setAppId(sharedUser.userId);
addPackageSettingLPw(p, sharedUser);
} else if (idObj != null) {
- String msg = "Bad package setting: package " + p.name + " has shared uid "
- + sharedUserId + " that is not a shared uid\n";
+ String msg = "Bad package setting: package " + p.getPackageName()
+ + " has shared uid " + sharedUserId + " that is not a shared uid\n";
mReadMessages.append(msg);
PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
} else {
- String msg = "Bad package setting: package " + p.name + " has shared uid "
- + sharedUserId + " that is not defined\n";
+ String msg = "Bad package setting: package " + p.getPackageName()
+ + " has shared uid " + sharedUserId + " that is not defined\n";
mReadMessages.append(msg);
PackageManagerService.reportSettingsProblem(Log.ERROR, msg);
}
@@ -3048,9 +3049,9 @@
final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator();
while (disabledIt.hasNext()) {
final PackageSetting disabledPs = disabledIt.next();
- final Object id = getSettingLPr(disabledPs.appId);
+ final Object id = getSettingLPr(disabledPs.getAppId());
if (id != null && id instanceof SharedUserSetting) {
- disabledPs.sharedUser = (SharedUserSetting) id;
+ disabledPs.setSharedUser((SharedUserSetting) id);
}
}
@@ -3071,15 +3072,15 @@
final PackageManagerInternal pmInternal =
LocalServices.getService(PackageManagerInternal.class);
for (PackageSetting ps : mPackages.values()) {
- if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0 && ps.pkg != null
- && !ps.pkg.getPreferredActivityFilters().isEmpty()) {
+ if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0 && ps.getPkg() != null
+ && !ps.getPkg().getPreferredActivityFilters().isEmpty()) {
List<Pair<String, ParsedIntentInfo>> intents
- = ps.pkg.getPreferredActivityFilters();
+ = ps.getPkg().getPreferredActivityFilters();
for (int i=0; i<intents.size(); i++) {
Pair<String, ParsedIntentInfo> pair = intents.get(i);
applyDefaultPreferredActivityLPw(pmInternal,
new WatchedIntentFilter(pair.second),
- new ComponentName(ps.name, pair.first), userId);
+ new ComponentName(ps.getPackageName(), pair.first), userId);
}
}
}
@@ -3461,12 +3462,12 @@
if (timeStamp == 0) {
timeStamp = parser.getAttributeLong(null, "ts", 0);
}
- ps.setTimeStamp(timeStamp);
- ps.firstInstallTime = parser.getAttributeLongHex(null, "it", 0);
- ps.lastUpdateTime = parser.getAttributeLongHex(null, "ut", 0);
- ps.appId = parser.getAttributeInt(null, "userId", 0);
- if (ps.appId <= 0) {
- ps.appId = parser.getAttributeInt(null, "sharedUserId", 0);
+ ps.setLastModifiedTime(timeStamp);
+ ps.setFirstInstallTime(parser.getAttributeLongHex(null, "it", 0));
+ ps.setLastUpdateTime(parser.getAttributeLongHex(null, "ut", 0));
+ ps.setAppId(parser.getAttributeInt(null, "userId", 0));
+ if (ps.getAppId() <= 0) {
+ ps.setAppId(parser.getAttributeInt(null, "sharedUserId", 0));
}
final float loadingProgress =
parser.getAttributeFloat(null, "loadingProgress", 0);
@@ -3654,9 +3655,9 @@
+ userId + " while parsing settings at "
+ parser.getPositionDescription());
} else {
- packageSetting.setTimeStamp(timeStamp);
- packageSetting.firstInstallTime = firstInstallTime;
- packageSetting.lastUpdateTime = lastUpdateTime;
+ packageSetting.setLastModifiedTime(timeStamp);
+ packageSetting.setFirstInstallTime(firstInstallTime);
+ packageSetting.setLastUpdateTime(lastUpdateTime);
}
} else if (sharedUserId != 0) {
if (sharedUserId > 0) {
@@ -3667,9 +3668,9 @@
null /*usesStaticLibraries*/,
null /*usesStaticLibraryVersions*/,
null /*mimeGroups*/, domainSetId);
- packageSetting.setTimeStamp(timeStamp);
- packageSetting.firstInstallTime = firstInstallTime;
- packageSetting.lastUpdateTime = lastUpdateTime;
+ packageSetting.setLastModifiedTime(timeStamp);
+ packageSetting.setFirstInstallTime(firstInstallTime);
+ packageSetting.setLastUpdateTime(lastUpdateTime);
mPendingPackages.add(packageSetting);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name
@@ -3695,15 +3696,15 @@
installInitiatingPackageName, installOriginatingPackageName,
installerPackageName, installerAttributionTag, isOrphaned,
installInitiatorUninstalled);
- packageSetting.installSource = installSource;
- packageSetting.volumeUuid = volumeUuid;
- packageSetting.categoryHint = categoryHint;
- packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr;
- packageSetting.primaryCpuAbiString = primaryCpuAbiString;
- packageSetting.secondaryCpuAbiString = secondaryCpuAbiString;
- packageSetting.updateAvailable = updateAvailable;
- packageSetting.forceQueryableOverride = installedForceQueryable;
- packageSetting.incrementalStates = new IncrementalStates(isLoading, loadingProgress);
+ packageSetting.setInstallSource(installSource)
+ .setVolumeUuid(volumeUuid)
+ .setCategoryOverride(categoryHint)
+ .setLegacyNativeLibraryPath(legacyNativeLibraryPathStr)
+ .setPrimaryCpuAbi(primaryCpuAbiString)
+ .setSecondaryCpuAbi(secondaryCpuAbiString)
+ .setUpdateAvailable(updateAvailable)
+ .setForceQueryableOverride(installedForceQueryable)
+ .setIncrementalStates(new IncrementalStates(isLoading, loadingProgress));
// Handle legacy string here for single-user mode
final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
if (enabledStr != null) {
@@ -3744,11 +3745,12 @@
} else if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
readEnabledComponentsLPw(packageSetting, parser, 0);
} else if (tagName.equals("sigs")) {
- packageSetting.signatures.readXml(parser, mPastSignatures.untrackedStorage());
+ packageSetting.getSignatures()
+ .readXml(parser,mPastSignatures.untrackedStorage());
} else if (tagName.equals(TAG_PERMISSIONS)) {
readInstallPermissionsLPr(parser,
packageSetting.getLegacyPermissionState(), users);
- packageSetting.installPermissionsFixed = true;
+ packageSetting.setInstallPermissionsFixed(true);
} else if (tagName.equals("proper-signing-keyset")) {
long id = parser.getAttributeLong(null, "identifier");
Integer refCt = mKeySetRefs.get(id);
@@ -3757,12 +3759,12 @@
} else {
mKeySetRefs.put(id, 1);
}
- packageSetting.keySetData.setProperSigningKeySet(id);
+ packageSetting.getKeySetData().setProperSigningKeySet(id);
} else if (tagName.equals("signing-keyset")) {
// from v1 of keysetmanagerservice - no longer used
} else if (tagName.equals("upgrade-keyset")) {
long id = parser.getAttributeLong(null, "identifier");
- packageSetting.keySetData.addUpgradeKeySetById(id);
+ packageSetting.getKeySetData().addUpgradeKeySetById(id);
} else if (tagName.equals("defined-keyset")) {
long id = parser.getAttributeLong(null, "identifier");
String alias = parser.getAttributeValue(null, "alias");
@@ -3772,15 +3774,17 @@
} else {
mKeySetRefs.put(id, 1);
}
- packageSetting.keySetData.addDefinedKeySet(id, alias);
+ packageSetting.getKeySetData().addDefinedKeySet(id, alias);
} else if (tagName.equals("install-initiator-sigs")) {
final PackageSignatures signatures = new PackageSignatures();
signatures.readXml(parser, mPastSignatures.untrackedStorage());
- packageSetting.installSource =
- packageSetting.installSource.setInitiatingPackageSignatures(signatures);
+ packageSetting.setInstallSource(
+ packageSetting.getInstallSource()
+ .setInitiatingPackageSignatures(signatures));
} else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
- mDomainVerificationManager.addLegacySetting(packageSetting.name, ivi);
+ mDomainVerificationManager.addLegacySetting(packageSetting.getPackageName(),
+ ivi);
if (DEBUG_PARSER) {
Log.d(TAG, "Read domain verification for package: " + ivi.getPackageName());
}
@@ -3991,19 +3995,20 @@
Trace.TRACE_TAG_PACKAGE_MANAGER);
t.traceBegin("createNewUser-" + userHandle);
Installer.Batch batch = new Installer.Batch();
- final boolean skipPackageWhitelist = userTypeInstallablePackages == null;
+ final boolean skipPackageAllowList = userTypeInstallablePackages == null;
synchronized (mLock) {
final int size = mPackages.size();
for (int i = 0; i < size; i++) {
final PackageSetting ps = mPackages.valueAt(i);
- if (ps.pkg == null) {
+ if (ps.getPkg() == null) {
continue;
}
final boolean shouldMaybeInstall = ps.isSystem() &&
- !ArrayUtils.contains(disallowedPackages, ps.name) &&
+ !ArrayUtils.contains(disallowedPackages, ps.getPackageName()) &&
!ps.getPkgState().isHiddenUntilInstalled();
final boolean shouldReallyInstall = shouldMaybeInstall &&
- (skipPackageWhitelist || userTypeInstallablePackages.contains(ps.name));
+ (skipPackageAllowList || userTypeInstallablePackages.contains(
+ ps.getPackageName()));
// Only system apps are initially installed.
ps.setInstalled(shouldReallyInstall, userHandle);
// If userTypeInstallablePackages is the *only* reason why we're not installing,
@@ -4016,10 +4021,10 @@
// Need to create a data directory for all apps installed for this user.
// Accumulate all required args and call the installer after mPackages lock
// has been released
- final String seInfo = AndroidPackageUtils.getSeInfo(ps.pkg, ps);
- batch.createAppData(ps.volumeUuid, ps.name, userHandle,
+ final String seInfo = AndroidPackageUtils.getSeInfo(ps.getPkg(), ps);
+ batch.createAppData(ps.getVolumeUuid(), ps.getPackageName(), userHandle,
StorageManager.FLAG_STORAGE_CE | StorageManager.FLAG_STORAGE_DE,
- ps.appId, seInfo, ps.pkg.getTargetSdkVersion());
+ ps.getAppId(), seInfo, ps.getPkg().getTargetSdkVersion());
} else {
// Make sure the app is excluded from storage mapping for this user
writeKernelMappingLPr(ps);
@@ -4145,7 +4150,7 @@
if (enabledPackageSetting == null) {
return null;
}
- return getDisabledSystemPkgLPr(enabledPackageSetting.name);
+ return getDisabledSystemPkgLPr(enabledPackageSetting.getPackageName());
}
boolean isEnabledAndMatchLPr(ComponentInfo componentInfo, int flags, int userId) {
@@ -4171,7 +4176,7 @@
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
- return pkg.installSource.isOrphaned;
+ return pkg.getInstallSource().isOrphaned;
}
int getApplicationEnabledSettingLPr(String packageName, int userId)
@@ -4219,9 +4224,9 @@
pkgSetting.setStopped(stopped, userId);
// pkgSetting.pkg.mSetStopped = stopped;
if (pkgSetting.getNotLaunched(userId)) {
- if (pkgSetting.installSource.installerPackageName != null) {
- pm.notifyFirstLaunch(pkgSetting.name,
- pkgSetting.installSource.installerPackageName, userId);
+ if (pkgSetting.getInstallSource().installerPackageName != null) {
+ pm.notifyFirstLaunch(pkgSetting.getPackageName(),
+ pkgSetting.getInstallSource().installerPackageName, userId);
}
pkgSetting.setNotLaunched(false, userId);
}
@@ -4300,7 +4305,7 @@
ArrayList<PackageSetting> res = new ArrayList<>();
for (int i = 0; i < mPackages.size(); i++) {
final PackageSetting setting = mPackages.valueAt(i);
- if (Objects.equals(volumeUuid, setting.volumeUuid)) {
+ if (Objects.equals(volumeUuid, setting.getVolumeUuid())) {
res.add(setting);
}
}
@@ -4391,24 +4396,24 @@
ArraySet<String> permissionNames, PackageSetting ps,
LegacyPermissionState permissionsState, SimpleDateFormat sdf, Date date,
List<UserInfo> users, boolean dumpAll, boolean dumpAllComponents) {
- AndroidPackage pkg = ps.pkg;
+ AndroidPackage pkg = ps.getPkg();
if (checkinTag != null) {
pw.print(checkinTag);
pw.print(",");
- pw.print(ps.realName != null ? ps.realName : ps.name);
+ pw.print(ps.getRealName() != null ? ps.getRealName() : ps.getPackageName());
pw.print(",");
- pw.print(ps.appId);
+ pw.print(ps.getAppId());
pw.print(",");
- pw.print(ps.versionCode);
+ pw.print(ps.getLongVersionCode());
pw.print(",");
- pw.print(ps.firstInstallTime);
+ pw.print(ps.getFirstInstallTime());
pw.print(",");
- pw.print(ps.lastUpdateTime);
+ pw.print(ps.getLastUpdateTime());
pw.print(",");
- pw.print(ps.installSource.installerPackageName != null
- ? ps.installSource.installerPackageName : "?");
- pw.print(ps.installSource.installerAttributionTag != null
- ? "(" + ps.installSource.installerAttributionTag + ")" : "");
+ pw.print(ps.getInstallSource().installerPackageName != null
+ ? ps.getInstallSource().installerPackageName : "?");
+ pw.print(ps.getInstallSource().installerAttributionTag != null
+ ? "(" + ps.getInstallSource().installerAttributionTag + ")" : "");
pw.println();
if (pkg != null) {
pw.print(checkinTag); pw.print("-"); pw.print("splt,");
@@ -4436,7 +4441,7 @@
pw.print(ps.getStopped(user.id) ? "S" : "s");
pw.print(ps.getNotLaunched(user.id) ? "l" : "L");
pw.print(ps.getInstantApp(user.id) ? "IA" : "ia");
- pw.print(ps.getVirtulalPreload(user.id) ? "VPI" : "vpi");
+ pw.print(ps.getVirtualPreload(user.id) ? "VPI" : "vpi");
String harmfulAppWarning = ps.getHarmfulAppWarning(user.id);
pw.print(harmfulAppWarning != null ? "HA" : "ha");
pw.print(",");
@@ -4451,35 +4456,35 @@
}
pw.print(prefix); pw.print("Package [");
- pw.print(ps.realName != null ? ps.realName : ps.name);
+ pw.print(ps.getRealName() != null ? ps.getRealName() : ps.getPackageName());
pw.print("] (");
pw.print(Integer.toHexString(System.identityHashCode(ps)));
pw.println("):");
- if (ps.realName != null) {
+ if (ps.getRealName() != null) {
pw.print(prefix); pw.print(" compat name=");
- pw.println(ps.name);
+ pw.println(ps.getPackageName());
}
- pw.print(prefix); pw.print(" userId="); pw.println(ps.appId);
+ pw.print(prefix); pw.print(" userId="); pw.println(ps.getAppId());
- if (ps.sharedUser != null) {
- pw.print(prefix); pw.print(" sharedUser="); pw.println(ps.sharedUser);
+ if (ps.getSharedUser() != null) {
+ pw.print(prefix); pw.print(" sharedUser="); pw.println(ps.getSharedUser());
}
pw.print(prefix); pw.print(" pkg="); pw.println(pkg);
pw.print(prefix); pw.print(" codePath="); pw.println(ps.getPathString());
if (permissionNames == null) {
pw.print(prefix); pw.print(" resourcePath="); pw.println(ps.getPathString());
pw.print(prefix); pw.print(" legacyNativeLibraryDir=");
- pw.println(ps.legacyNativeLibraryPathString);
+ pw.println(ps.getLegacyNativeLibraryPath());
pw.print(prefix); pw.print(" extractNativeLibs=");
pw.println((ps.pkgFlags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0
? "true" : "false");
- pw.print(prefix); pw.print(" primaryCpuAbi="); pw.println(ps.primaryCpuAbiString);
- pw.print(prefix); pw.print(" secondaryCpuAbi="); pw.println(ps.secondaryCpuAbiString);
- pw.print(prefix); pw.print(" cpuAbiOverride="); pw.println(ps.cpuAbiOverrideString);
+ pw.print(prefix); pw.print(" primaryCpuAbi="); pw.println(ps.getPrimaryCpuAbi());
+ pw.print(prefix); pw.print(" secondaryCpuAbi="); pw.println(ps.getSecondaryCpuAbi());
+ pw.print(prefix); pw.print(" cpuAbiOverride="); pw.println(ps.getCpuAbiOverride());
}
- pw.print(prefix); pw.print(" versionCode="); pw.print(ps.versionCode);
+ pw.print(prefix); pw.print(" versionCode="); pw.print(ps.getLongVersionCode());
if (pkg != null) {
pw.print(" minSdk="); pw.print(pkg.getMinSdkVersion());
pw.print(" targetSdk="); pw.println(pkg.getTargetSdkVersion());
@@ -4518,16 +4523,19 @@
pw.print(prefix); pw.print(" hasPreserveLegacyExternalStorage=true");
pw.println();
}
- pw.print(prefix); pw.print(" forceQueryable="); pw.print(ps.pkg.isForceQueryable());
- if (ps.forceQueryableOverride) {
+ pw.print(prefix); pw.print(" forceQueryable=");
+ pw.print(ps.getPkg().isForceQueryable());
+ if (ps.isForceQueryableOverride()) {
pw.print(" (override=true)");
}
pw.println();
- if (ps.pkg.getQueriesPackages().isEmpty()) {
- pw.append(prefix).append(" queriesPackages=").println(ps.pkg.getQueriesPackages());
+ if (ps.getPkg().getQueriesPackages().isEmpty()) {
+ pw.append(prefix).append(" queriesPackages=")
+ .println(ps.getPkg().getQueriesPackages());
}
- if (!ps.pkg.getQueriesIntents().isEmpty()) {
- pw.append(prefix).append(" queriesIntents=").println(ps.pkg.getQueriesIntents());
+ if (!ps.getPkg().getQueriesIntents().isEmpty()) {
+ pw.append(prefix).append(" queriesIntents=")
+ .println(ps.getPkg().getQueriesIntents());
}
File dataDir = PackageInfoWithoutStateUtils.getDataDir(pkg, UserHandle.myUserId());
pw.print(prefix); pw.print(" dataDir="); pw.println(dataDir.getAbsolutePath());
@@ -4652,33 +4660,34 @@
}
}
pw.print(prefix); pw.print(" timeStamp=");
- date.setTime(ps.timeStamp);
+ date.setTime(ps.getLastModifiedTime());
pw.println(sdf.format(date));
pw.print(prefix); pw.print(" firstInstallTime=");
- date.setTime(ps.firstInstallTime);
+ date.setTime(ps.getFirstInstallTime());
pw.println(sdf.format(date));
pw.print(prefix); pw.print(" lastUpdateTime=");
- date.setTime(ps.lastUpdateTime);
+ date.setTime(ps.getLastUpdateTime());
pw.println(sdf.format(date));
- if (ps.installSource.installerPackageName != null) {
+ if (ps.getInstallSource().installerPackageName != null) {
pw.print(prefix); pw.print(" installerPackageName=");
- pw.println(ps.installSource.installerPackageName);
+ pw.println(ps.getInstallSource().installerPackageName);
}
- if (ps.installSource.installerAttributionTag != null) {
+ if (ps.getInstallSource().installerAttributionTag != null) {
pw.print(prefix); pw.print(" installerAttributionTag=");
- pw.println(ps.installSource.installerAttributionTag);
+ pw.println(ps.getInstallSource().installerAttributionTag);
}
if (ps.isPackageLoading()) {
- pw.print(prefix); pw.println(" loadingProgress="
- + (int) (ps.getIncrementalStates().getProgress() * 100) + "%");
+ pw.print(prefix); pw.println(" loadingProgress=" +
+ (int) (ps.getIncrementalStates().getIncrementalStatesInfo().getProgress()
+ * 100) + "%");
}
- if (ps.volumeUuid != null) {
+ if (ps.getVolumeUuid() != null) {
pw.print(prefix); pw.print(" volumeUuid=");
- pw.println(ps.volumeUuid);
+ pw.println(ps.getVolumeUuid());
}
- pw.print(prefix); pw.print(" signatures="); pw.println(ps.signatures);
+ pw.print(prefix); pw.print(" signatures="); pw.println(ps.getSignatures());
pw.print(prefix); pw.print(" installPermissionsFixed=");
- pw.print(ps.installPermissionsFixed);
+ pw.print(ps.isInstallPermissionsFixed());
pw.println();
pw.print(prefix); pw.print(" pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC);
pw.println();
@@ -4728,7 +4737,7 @@
}
}
- if (ps.sharedUser == null || permissionNames != null || dumpAll) {
+ if (ps.getSharedUser() == null || permissionNames != null || dumpAll) {
dumpInstallPermissionsLPr(pw, prefix + " ", permissionNames, permissionsState, users);
}
@@ -4757,7 +4766,7 @@
pw.print(" instant=");
pw.print(ps.getInstantApp(user.id));
pw.print(" virtual=");
- pw.println(ps.getVirtulalPreload(user.id));
+ pw.println(ps.getVirtualPreload(user.id));
if (ps.getSuspended(user.id)) {
pw.print(prefix);
@@ -4838,9 +4847,9 @@
pw.println(lastDisabledAppCaller);
}
- if (ps.sharedUser == null) {
+ if (ps.getSharedUser() == null) {
dumpGidsLPr(pw, prefix + " ", mPermissionDataProvider.getGidsForUid(
- UserHandle.getUid(user.id, ps.appId)));
+ UserHandle.getUid(user.id, ps.getAppId())));
dumpRuntimePermissionsLPr(pw, prefix + " ", permissionNames, permissionsState
.getPermissionStates(user.id), dumpAll);
}
@@ -4879,19 +4888,19 @@
dumpState.isOptionEnabled(DumpState.OPTION_DUMP_ALL_COMPONENTS);
List<UserInfo> users = getAllUsers(UserManagerService.getInstance());
for (final PackageSetting ps : mPackages.values()) {
- if (packageName != null && !packageName.equals(ps.realName)
- && !packageName.equals(ps.name)) {
+ if (packageName != null && !packageName.equals(ps.getRealName())
+ && !packageName.equals(ps.getPackageName())) {
continue;
}
final LegacyPermissionState permissionsState =
- mPermissionDataProvider.getLegacyPermissionState(ps.appId);
+ mPermissionDataProvider.getLegacyPermissionState(ps.getAppId());
if (permissionNames != null
&& !permissionsState.hasPermissionState(permissionNames)) {
continue;
}
if (!checkin && packageName != null) {
- dumpState.setSharedUser(ps.sharedUser);
+ dumpState.setSharedUser(ps.getSharedUser());
}
if (!checkin && !printedSomething) {
@@ -4931,8 +4940,8 @@
printedSomething = false;
if (mDisabledSysPackages.size() > 0 && permissionNames == null) {
for (final PackageSetting ps : mDisabledSysPackages.values()) {
- if (packageName != null && !packageName.equals(ps.realName)
- && !packageName.equals(ps.name)) {
+ if (packageName != null && !packageName.equals(ps.getRealName())
+ && !packageName.equals(ps.getPackageName())) {
continue;
}
if (!checkin && !printedSomething) {
@@ -4942,7 +4951,7 @@
printedSomething = true;
}
final LegacyPermissionState permissionsState =
- mPermissionDataProvider.getLegacyPermissionState(ps.appId);
+ mPermissionDataProvider.getLegacyPermissionState(ps.getAppId());
dumpPackageLPr(pw, " ", checkin ? "dis" : null, permissionNames, ps,
permissionsState, sdf, date, users, packageName != null, dumpAllComponents);
}
@@ -5184,11 +5193,11 @@
}
void dumpComponents(PrintWriter pw, String prefix, PackageSetting ps) {
- dumpComponents(pw, prefix, "activities:", ps.pkg.getActivities());
- dumpComponents(pw, prefix, "services:", ps.pkg.getServices());
- dumpComponents(pw, prefix, "receivers:", ps.pkg.getReceivers());
- dumpComponents(pw, prefix, "providers:", ps.pkg.getProviders());
- dumpComponents(pw, prefix, "instrumentations:", ps.pkg.getInstrumentations());
+ dumpComponents(pw, prefix, "activities:", ps.getPkg().getActivities());
+ dumpComponents(pw, prefix, "services:", ps.getPkg().getServices());
+ dumpComponents(pw, prefix, "receivers:", ps.getPkg().getReceivers());
+ dumpComponents(pw, prefix, "providers:", ps.getPkg().getProviders());
+ dumpComponents(pw, prefix, "instrumentations:", ps.getPkg().getInstrumentations());
}
void dumpComponents(PrintWriter pw, String prefix, String label,
@@ -5410,11 +5419,11 @@
for (int i = 0; i < packagesSize; i++) {
String packageName = mPackages.keyAt(i);
PackageSetting packageSetting = mPackages.valueAt(i);
- if (packageSetting.sharedUser == null) {
+ if (packageSetting.getSharedUser() == null) {
List<RuntimePermissionsState.PermissionState> permissions =
getPermissionsFromPermissionsState(
packageSetting.getLegacyPermissionState(), userId);
- if (permissions.isEmpty() && !packageSetting.areInstallPermissionsFixed()) {
+ if (permissions.isEmpty() && !packageSetting.isInstallPermissionsFixed()) {
// Storing an empty state means the package is known to the system and its
// install permissions have been granted and fixed. If this is not the case,
// we should not store anything.
@@ -5506,8 +5515,8 @@
if (permissions != null) {
readPermissionsStateLpr(permissions, packageSetting.getLegacyPermissionState(),
userId);
- packageSetting.installPermissionsFixed = true;
- } else if (packageSetting.sharedUser == null && !isUpgradeToR) {
+ packageSetting.setInstallPermissionsFixed(true);
+ } else if (packageSetting.getSharedUser() == null && !isUpgradeToR) {
Slog.w(TAG, "Missing permission state for package: " + packageName);
packageSetting.getLegacyPermissionState().setMissing(true, userId);
}
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 8bfa728..15df249 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -161,16 +161,16 @@
void addPackage(PackageSetting packageSetting) {
// If this is the first package added to this shared user, temporarily (until next boot) use
// its targetSdkVersion when assigning seInfo for the shared user.
- if ((packages.size() == 0) && (packageSetting.pkg != null)) {
- seInfoTargetSdkVersion = packageSetting.pkg.getTargetSdkVersion();
+ if ((packages.size() == 0) && (packageSetting.getPkg() != null)) {
+ seInfoTargetSdkVersion = packageSetting.getPkg().getTargetSdkVersion();
}
if (packages.add(packageSetting)) {
setFlags(this.pkgFlags | packageSetting.pkgFlags);
setPrivateFlags(this.pkgPrivateFlags | packageSetting.pkgPrivateFlags);
onChanged();
}
- if (packageSetting.pkg != null) {
- addProcesses(packageSetting.pkg.getProcesses());
+ if (packageSetting.getPkg() != null) {
+ addProcesses(packageSetting.getPkg().getProcesses());
}
}
@@ -180,10 +180,10 @@
}
final ArrayList<AndroidPackage> pkgList = new ArrayList<>(packages.size());
for (PackageSetting ps : packages) {
- if ((ps == null) || (ps.pkg == null)) {
+ if ((ps == null) || (ps.getPkg() == null)) {
continue;
}
- pkgList.add(ps.pkg);
+ pkgList.add(ps.getPkg());
}
return pkgList;
}
@@ -203,21 +203,21 @@
return;
}
for (PackageSetting ps : packages) {
- if ((ps == null) || (ps.pkg == null)) {
+ if ((ps == null) || (ps.getPkg() == null)) {
continue;
}
- if (ps.pkg.getTargetSdkVersion() < seInfoTargetSdkVersion) {
- seInfoTargetSdkVersion = ps.pkg.getTargetSdkVersion();
+ if (ps.getPkg().getTargetSdkVersion() < seInfoTargetSdkVersion) {
+ seInfoTargetSdkVersion = ps.getPkg().getTargetSdkVersion();
onChanged();
}
}
for (PackageSetting ps : packages) {
- if ((ps == null) || (ps.pkg == null)) {
+ if ((ps == null) || (ps.getPkg() == null)) {
continue;
}
- final boolean isPrivileged = isPrivileged() | ps.pkg.isPrivileged();
- ps.getPkgState().setOverrideSeInfo(SELinuxMMAC.getSeInfo(ps.pkg, isPrivileged,
+ final boolean isPrivileged = isPrivileged() | ps.getPkg().isPrivileged();
+ ps.getPkgState().setOverrideSeInfo(SELinuxMMAC.getSeInfo(ps.getPkg(), isPrivileged,
seInfoTargetSdkVersion));
onChanged();
}
@@ -229,7 +229,7 @@
public void updateProcesses() {
processes.clear();
for (int i = packages.size() - 1; i >= 0; i--) {
- final AndroidPackage pkg = packages.valueAt(i).pkg;
+ final AndroidPackage pkg = packages.valueAt(i).getPkg();
if (pkg != null) {
addProcesses(pkg.getProcesses());
}
@@ -256,7 +256,7 @@
/** Updates all fields in this shared user setting from another. */
public SharedUserSetting updateFrom(SharedUserSetting sharedUser) {
- copyFrom(sharedUser);
+ super.copySettingBase(sharedUser);
this.userId = sharedUser.userId;
this.uidFlags = sharedUser.uidFlags;
this.uidPrivateFlags = sharedUser.uidPrivateFlags;
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 4dfb6b8..a2f48c7 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -496,7 +496,7 @@
long ceDataInode = -1;
final PackageSetting ps = mPmi.getPackageSetting(packageName);
if (ps != null) {
- appId = ps.appId;
+ appId = ps.getAppId();
ceDataInode = ps.getCeDataInode(UserHandle.USER_SYSTEM);
// NOTE: We ignore the user specified in the InstallParam because we know this is
// an update, and hence need to restore data for all installed users.
diff --git a/services/core/java/com/android/server/pm/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index a70df91..d44861d 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -52,12 +52,14 @@
import java.util.List;
/** Helper class to handle storage events and private apps loading */
-public class StorageEventHelper extends StorageEventListener {
- final PackageManagerService mPm;
+public final class StorageEventHelper extends StorageEventListener {
+ private final PackageManagerService mPm;
+ private final BroadcastHelper mBroadcastHelper;
// TODO(b/198166813): remove PMS dependency
public StorageEventHelper(PackageManagerService pm) {
mPm = pm;
+ mBroadcastHelper = new BroadcastHelper(mPm.mInjector);
}
@Override
@@ -94,15 +96,16 @@
synchronized (mPm.mLock) {
final List<PackageSetting> packages = mPm.mSettings.getVolumePackagesLPr(fsUuid);
for (PackageSetting ps : packages) {
- Slog.d(TAG, "Destroying " + ps.name + " because volume was forgotten");
- mPm.deletePackageVersioned(new VersionedPackage(ps.name,
+ Slog.d(TAG, "Destroying " + ps.getPackageName()
+ + " because volume was forgotten");
+ mPm.deletePackageVersioned(new VersionedPackage(ps.getPackageName(),
PackageManager.VERSION_CODE_HIGHEST),
new PackageManager.LegacyPackageDeleteObserver(null).getBinder(),
UserHandle.USER_SYSTEM, PackageManager.DELETE_ALL_USERS);
// Try very hard to release any references to this package
// so we don't risk the system server being killed due to
// open FDs
- AttributeCache.instance().removePackage(ps.name);
+ AttributeCache.instance().removePackage(ps.getPackageName());
}
mPm.mSettings.onVolumeForgotten(fsUuid);
@@ -134,7 +137,7 @@
}
for (PackageSetting ps : packages) {
- freezers.add(mPm.freezePackage(ps.name, "loadPrivatePackagesInner"));
+ freezers.add(mPm.freezePackage(ps.getPackageName(), "loadPrivatePackagesInner"));
synchronized (mPm.mInstallLock) {
final AndroidPackage pkg;
try {
@@ -148,7 +151,7 @@
if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
mPm.clearAppDataLIF(
- ps.pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
+ ps.getPkg(), UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
| FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY
| Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
}
@@ -226,16 +229,16 @@
final List<PackageSetting> packages =
mPm.mSettings.getVolumePackagesLPr(volumeUuid);
for (PackageSetting ps : packages) {
- if (ps.pkg == null) continue;
+ if (ps.getPkg() == null) continue;
- final AndroidPackage pkg = ps.pkg;
+ final AndroidPackage pkg = ps.getPkg();
final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
final PackageRemovedInfo outInfo = new PackageRemovedInfo(mPm);
- try (PackageFreezer freezer = mPm.freezePackageForDelete(ps.name, deleteFlags,
- "unloadPrivatePackagesInner")) {
- if (mPm.deletePackageLIF(ps.name, null, false, userIds, deleteFlags,
- outInfo, false)) {
+ try (PackageFreezer freezer = mPm.freezePackageForDelete(ps.getPackageName(),
+ deleteFlags, "unloadPrivatePackagesInner")) {
+ if (mPm.deletePackageLIF(ps.getPackageName(), null, false, userIds,
+ deleteFlags, outInfo, false)) {
unloaded.add(pkg);
} else {
Slog.w(TAG, "Failed to unload " + ps.getPath());
@@ -245,7 +248,7 @@
// Try very hard to release any references to this package
// so we don't risk the system server being killed due to
// open FDs
- AttributeCache.instance().removePackage(ps.name);
+ AttributeCache.instance().removePackage(ps.getPackageName());
}
mPm.writeSettingsLPrTEMP();
@@ -278,8 +281,8 @@
packageNames[i] = pkg.getPackageName();
packageUids[i] = pkg.getUid();
}
- mPm.sendResourcesChangedBroadcast(mediaStatus, replacing, packageNames, packageUids,
- finishedReceiver);
+ mBroadcastHelper.sendResourcesChangedBroadcast(mediaStatus, replacing, packageNames,
+ packageUids, finishedReceiver);
}
/**
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 844833b..30c77f9 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -859,11 +859,12 @@
@Override
public List<UserInfo> getProfiles(@UserIdInt int userId, boolean enabledOnly) {
- boolean returnFullInfo = true;
+ boolean returnFullInfo;
if (userId != UserHandle.getCallingUserId()) {
checkManageOrCreateUsersPermission("getting profiles related to user " + userId);
+ returnFullInfo = true;
} else {
- returnFullInfo = hasManageUsersPermission();
+ returnFullInfo = hasManageOrCreateUsersPermission();
}
final long ident = Binder.clearCallingIdentity();
try {
@@ -1660,9 +1661,13 @@
}
@Override
- public boolean isRestricted() {
+ public boolean isRestricted(@UserIdInt int userId) {
+ if (userId != UserHandle.getCallingUserId()) {
+ checkManageOrCreateUsersPermission("query isRestricted for user " + userId);
+ }
synchronized (mUsersLock) {
- return getUserInfoLU(UserHandle.getCallingUserId()).isRestricted();
+ final UserInfo userInfo = getUserInfoLU(userId);
+ return userInfo == null ? false : userInfo.isRestricted();
}
}
@@ -1683,15 +1688,14 @@
}
@Override
- public boolean hasRestrictedProfiles() {
+ public boolean hasRestrictedProfiles(@UserIdInt int userId) {
checkManageUsersPermission("hasRestrictedProfiles");
- final int callingUserId = UserHandle.getCallingUserId();
synchronized (mUsersLock) {
final int userSize = mUsers.size();
for (int i = 0; i < userSize; i++) {
UserInfo profile = mUsers.valueAt(i).info;
- if (callingUserId != profile.id
- && profile.restrictedProfileParentId == callingUserId) {
+ if (userId != profile.id
+ && profile.restrictedProfileParentId == userId) {
return true;
}
}
@@ -2542,6 +2546,14 @@
*/
private static final boolean hasManageUsersPermission() {
final int callingUid = Binder.getCallingUid();
+ return hasManageUsersPermission(callingUid);
+ }
+
+ /**
+ * @return whether the given UID is system UID or root's UID or the has the permission
+ * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS}.
+ */
+ private static boolean hasManageUsersPermission(int callingUid) {
return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
|| callingUid == Process.ROOT_UID
|| hasPermissionGranted(android.Manifest.permission.MANAGE_USERS, callingUid);
@@ -2553,9 +2565,7 @@
*/
private static final boolean hasManageUsersOrPermission(String alternativePermission) {
final int callingUid = Binder.getCallingUid();
- return UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
- || callingUid == Process.ROOT_UID
- || hasPermissionGranted(android.Manifest.permission.MANAGE_USERS, callingUid)
+ return hasManageUsersPermission(callingUid)
|| hasPermissionGranted(alternativePermission, callingUid);
}
@@ -4943,39 +4953,39 @@
}
@Override
- public String getSeedAccountName() throws RemoteException {
+ public String getSeedAccountName(@UserIdInt int userId) throws RemoteException {
checkManageUsersPermission("Cannot get seed account information");
synchronized (mUsersLock) {
- UserData userData = getUserDataLU(UserHandle.getCallingUserId());
- return userData.seedAccountName;
+ final UserData userData = getUserDataLU(userId);
+ return userData == null ? null : userData.seedAccountName;
}
}
@Override
- public String getSeedAccountType() throws RemoteException {
+ public String getSeedAccountType(@UserIdInt int userId) throws RemoteException {
checkManageUsersPermission("Cannot get seed account information");
synchronized (mUsersLock) {
- UserData userData = getUserDataLU(UserHandle.getCallingUserId());
- return userData.seedAccountType;
+ final UserData userData = getUserDataLU(userId);
+ return userData == null ? null : userData.seedAccountType;
}
}
@Override
- public PersistableBundle getSeedAccountOptions() throws RemoteException {
+ public PersistableBundle getSeedAccountOptions(@UserIdInt int userId) throws RemoteException {
checkManageUsersPermission("Cannot get seed account information");
synchronized (mUsersLock) {
- UserData userData = getUserDataLU(UserHandle.getCallingUserId());
- return userData.seedAccountOptions;
+ final UserData userData = getUserDataLU(userId);
+ return userData == null ? null : userData.seedAccountOptions;
}
}
@Override
- public void clearSeedAccountData() throws RemoteException {
+ public void clearSeedAccountData(@UserIdInt int userId) throws RemoteException {
checkManageUsersPermission("Cannot clear seed account information");
synchronized (mPackagesLock) {
UserData userData;
synchronized (mUsersLock) {
- userData = getUserDataLU(UserHandle.getCallingUserId());
+ userData = getUserDataLU(userId);
if (userData == null) return;
userData.clearSeedAccountData();
}
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 849d1a338..a1a6f5a 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -215,7 +215,7 @@
for (int userId : mUm.getUserIds()) {
final Set<String> userWhitelist = getInstallablePackagesForUserId(userId);
pmInt.forEachPackageSetting(pkgSetting -> {
- AndroidPackage pkg = pkgSetting.pkg;
+ AndroidPackage pkg = pkgSetting.getPkg();
if (pkg == null || !pkg.isSystem()) {
return;
}
@@ -262,7 +262,7 @@
== PackageManager.UNINSTALL_REASON_USER_TYPE;
} else {
// Only proceed with uninstall if the package is new to the device.
- return isFirstBoot || (isUpgrade && !preOtaPkgs.contains(pkgSetting.name));
+ return isFirstBoot || (isUpgrade && !preOtaPkgs.contains(pkgSetting.getPackageName()));
}
}
diff --git a/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
new file mode 100644
index 0000000..0418afb
--- /dev/null
+++ b/services/core/java/com/android/server/pm/parsing/library/ApexSharedLibraryUpdater.java
@@ -0,0 +1,67 @@
+/*
+ * 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.pm.parsing.library;
+
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemConfig;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+/**
+ * Updates packages to add or remove dependencies on shared libraries as per attributes
+ * in the library declaration
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class ApexSharedLibraryUpdater extends PackageSharedLibraryUpdater {
+
+ /**
+ * ArrayMap like the one you find in {@link SystemConfig}. The keys are the library names.
+ */
+ private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries;
+
+ public ApexSharedLibraryUpdater(
+ ArrayMap<String, SystemConfig.SharedLibraryEntry> sharedLibraries) {
+ mSharedLibraries = sharedLibraries;
+ }
+
+ @Override
+ public void updatePackage(ParsedPackage parsedPackage, boolean isUpdatedSystemApp) {
+ final int builtInLibCount = mSharedLibraries.size();
+ for (int i = 0; i < builtInLibCount; i++) {
+ updateSharedLibraryForPackage(mSharedLibraries.valueAt(i), parsedPackage);
+ }
+ }
+
+ private void updateSharedLibraryForPackage(SystemConfig.SharedLibraryEntry entry,
+ ParsedPackage parsedPackage) {
+ if (entry.onBootclasspathBefore != 0
+ && parsedPackage.getTargetSdkVersion() < entry.onBootclasspathBefore) {
+ // this package targets an API where this library was in the BCP, so add
+ // the library transparently in case the package is using it
+ prefixRequiredLibrary(parsedPackage, entry.name);
+ }
+
+ if (entry.canBeSafelyIgnored) {
+ // the library is now present in the BCP and always available; we don't need to add
+ // it a second time
+ removeLibrary(parsedPackage, entry.name);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
index 4334bf6..bbf584d 100644
--- a/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
+++ b/services/core/java/com/android/server/pm/parsing/library/PackageBackwardCompatibility.java
@@ -25,6 +25,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemConfig;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.util.ArrayList;
@@ -63,6 +64,11 @@
boolean bootClassPathContainsATB = !addUpdaterForAndroidTestBase(packageUpdaters);
+ // ApexSharedLibraryUpdater should be the last one, to allow modifications introduced by
+ // mainline after dessert release.
+ packageUpdaters.add(new ApexSharedLibraryUpdater(
+ SystemConfig.getInstance().getSharedLibraries()));
+
PackageSharedLibraryUpdater[] updaterArray = packageUpdaters
.toArray(new PackageSharedLibraryUpdater[0]);
INSTANCE = new PackageBackwardCompatibility(
@@ -106,6 +112,11 @@
private final PackageSharedLibraryUpdater[] mPackageUpdaters;
+ @VisibleForTesting
+ PackageSharedLibraryUpdater[] getPackageUpdaters() {
+ return mPackageUpdaters;
+ }
+
private PackageBackwardCompatibility(
boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) {
this.mBootClassPathContainsATB = bootClassPathContainsATB;
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index 5a8e793..55911b6 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -264,20 +264,20 @@
}
public static String getPrimaryCpuAbi(AndroidPackage pkg, @Nullable PackageSetting pkgSetting) {
- if (pkgSetting == null || TextUtils.isEmpty(pkgSetting.primaryCpuAbiString)) {
+ if (pkgSetting == null || TextUtils.isEmpty(pkgSetting.getPrimaryCpuAbi())) {
return getRawPrimaryCpuAbi(pkg);
}
- return pkgSetting.primaryCpuAbiString;
+ return pkgSetting.getPrimaryCpuAbi();
}
public static String getSecondaryCpuAbi(AndroidPackage pkg,
@Nullable PackageSetting pkgSetting) {
- if (pkgSetting == null || TextUtils.isEmpty(pkgSetting.secondaryCpuAbiString)) {
+ if (pkgSetting == null || TextUtils.isEmpty(pkgSetting.getSecondaryCpuAbi())) {
return getRawSecondaryCpuAbi(pkg);
}
- return pkgSetting.secondaryCpuAbiString;
+ return pkgSetting.getSecondaryCpuAbi();
}
/**
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index e503f21..d54c571 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -222,6 +222,12 @@
/** All nearby devices permissions */
private static final List<String> NEARBY_DEVICES_PERMISSIONS = new ArrayList<>();
+ /**
+ * All permissions that should be granted with the REVOKE_WHEN_REQUESTED flag, if they are
+ * implicitly added to a package
+ */
+ private static final List<String> IMPLICIT_GRANTED_PERMISSIONS = new ArrayList<>();
+
/** If the permission of the value is granted, so is the key */
private static final Map<String, String> FULLER_PERMISSION_MAP = new HashMap<>();
@@ -240,6 +246,7 @@
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_ADVERTISE);
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_CONNECT);
NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_SCAN);
+ IMPLICIT_GRANTED_PERMISSIONS.add(Manifest.permission.POST_NOTIFICATIONS);
}
/** Set of source package names for Privileged Permission Allowlist */
@@ -2739,7 +2746,7 @@
boolean changedInstallPermission = false;
if (replace) {
- userState.setInstallPermissionsFixed(ps.name, false);
+ userState.setInstallPermissionsFixed(ps.getPackageName(), false);
if (!ps.isSharedUser()) {
origState = new UidPermissionState(uidState);
uidState.reset();
@@ -2865,7 +2872,8 @@
// If this is an existing, non-system package, then
// we can't add any new permissions to it. Runtime
// permissions can be added any time - they are dynamic.
- if (!ps.isSystem() && userState.areInstallPermissionsFixed(ps.name)) {
+ if (!ps.isSystem() && userState.areInstallPermissionsFixed(
+ ps.getPackageName())) {
// Except... if this is a permission that was added
// to the platform (note: need to only do this when
// updating the platform).
@@ -3037,12 +3045,12 @@
}
if ((changedInstallPermission || replace)
- && !userState.areInstallPermissionsFixed(ps.name)
+ && !userState.areInstallPermissionsFixed(ps.getPackageName())
&& !ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
// This is the first that we have heard about this package, so the
// permissions we have now selected are fixed until explicitly
// changed.
- userState.setInstallPermissionsFixed(ps.name, true);
+ userState.setInstallPermissionsFixed(ps.getPackageName(), true);
}
updatedUserIds = revokePermissionsNoLongerImplicitLocked(uidState, pkg,
@@ -3309,6 +3317,22 @@
inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms, newPerm, ps,
pkg);
}
+ } else if (IMPLICIT_GRANTED_PERMISSIONS.contains(newPerm)
+ && !origPs.hasPermissionState(newPerm)) {
+ Permission bp = mRegistry.getPermission(newPerm);
+ if (bp == null) {
+ throw new IllegalStateException("Unknown new permission " + newPerm);
+ }
+ if ((ps.getPermissionState(newPerm).getFlags()
+ & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
+ // No need to grant if review is required
+ continue;
+ }
+ updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+ ps.updatePermissionFlags(bp,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
+ ps.grantPermission(bp);
}
}
@@ -3571,7 +3595,7 @@
if (pkgSetting.getPkgState().isUpdatedSystemApp()) {
final PackageSetting disabledPs = mPackageManagerInt
.getDisabledSystemPackage(pkg.getPackageName());
- final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.pkg;
+ final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.getPkg();
if (disabledPkg != null
&& ((isPrivilegedPermission && disabledPkg.isPrivileged())
|| (isOemPermission && canGrantOemPermission(disabledPkg,
@@ -3968,7 +3992,7 @@
PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage(
pkg.getPackageName());
- boolean isShadowingSystemPkg = disabledPs != null && disabledPs.appId == pkg.getUid();
+ boolean isShadowingSystemPkg = disabledPs != null && disabledPs.getAppId() == pkg.getUid();
boolean shouldKillUid = false;
// Update permissions
@@ -4680,7 +4704,8 @@
for (final int userId : userIds) {
final UserPermissionState userState = mState.getOrCreateUserState(userId);
- userState.setInstallPermissionsFixed(ps.name, ps.areInstallPermissionsFixed());
+ userState.setInstallPermissionsFixed(ps.getPackageName(),
+ ps.isInstallPermissionsFixed());
final UidPermissionState uidState = userState.getOrCreateUidState(appId);
uidState.reset();
uidState.setMissing(legacyState.isMissing(userId));
@@ -4725,14 +4750,14 @@
continue;
}
- if (userState.areInstallPermissionsFixed(ps.name)) {
+ if (userState.areInstallPermissionsFixed(ps.getPackageName())) {
ps.setInstallPermissionsFixed(true);
}
final UidPermissionState uidState = userState.getUidState(appId);
if (uidState == null) {
- Slog.e(TAG, "Missing permission state for " + ps.name + " and user "
- + userId);
+ Slog.e(TAG, "Missing permission state for " + ps.getPackageName()
+ + " and user " + userId);
continue;
}
@@ -4875,13 +4900,13 @@
return false;
}
if (!oldPs.isSystem()) {
- Slog.w(TAG, "Unable to update from " + oldPs.name
+ Slog.w(TAG, "Unable to update from " + oldPs.getPackageName()
+ " to " + newPkg.getPackageName()
+ ": old package not in system partition");
return false;
}
- if (mPackageManagerInt.getPackage(oldPs.name) != null) {
- Slog.w(TAG, "Unable to update from " + oldPs.name
+ if (mPackageManagerInt.getPackage(oldPs.getPackageName()) != null) {
+ Slog.w(TAG, "Unable to update from " + oldPs.getPackageName()
+ " to " + newPkg.getPackageName()
+ ": old package still exists");
return false;
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index 0d29bab..2ee8eae 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -54,8 +54,6 @@
* </ul>
*
* @hide
- *
- * TODO(chiuwinson): Delete all of the method defaults
*/
// TODO(b/173807334): Expose API
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
@@ -78,12 +76,12 @@
* app once the device reboots or otherwise re-scans it.
*/
@Nullable
- default AndroidPackageApi getAndroidPackage() { return null; }
+ AndroidPackageApi getAndroidPackage();
/**
* The non-user-specific UID
*/
- default int getAppId() { return -1; }
+ int getAppId();
/**
* Value set through {@link PackageManager#setApplicationCategoryHint(String, int)}. Only
@@ -91,33 +89,33 @@
*
* @see AndroidPackageApi#getCategory()
*/
- default int getCategoryOverride() { return -1; }
+ int getCategoryOverride();
@Nullable
- default String getCpuAbiOverride() { return null; }
+ String getCpuAbiOverride();
/**
* In epoch milliseconds.
*/
- default long getFirstInstallTime() { return -1; }
+ long getFirstInstallTime();
/**
* In epoch milliseconds.
*/
- default long getLastModifiedTime() { return -1; }
+ long getLastModifiedTime();
@NonNull
- default long[] getLastPackageUsageTime() { return null; }
+ long[] getLastPackageUsageTime();
/**
* In epoch milliseconds.
*/
- default long getLastUpdateTime() { return -1; }
+ long getLastUpdateTime();
/**
* @see AndroidPackageApi#getLongVersionCode()
*/
- default long getLongVersionCode() { return -1; }
+ long getLongVersionCode();
/**
* Maps mime group name to the set of Mime types in a group. Mime groups declared by app are
@@ -125,28 +123,28 @@
* thus keys in this map should not change
*/
@NonNull
- default Map<String, Set<String>> getMimeGroups() { return null; }
+ Map<String, Set<String>> getMimeGroups();
/**
* @see AndroidPackageApi#getPackageName()
*/
@NonNull
- default String getPackageName() { return null; }
+ String getPackageName();
/**
* @see AndroidPackageApi#getPath()
*/
@NonNull
- default File getPath() { return null; }
+ File getPath();
@Nullable
- default String getPrimaryCpuAbi() { return null; }
+ String getPrimaryCpuAbi();
@Nullable
- default String getSeInfoOverride() { return null; }
+ String getSeInfoOverride();
@Nullable
- default String getSecondaryCpuAbi() { return null; }
+ String getSecondaryCpuAbi();
/**
* Retrieves the shared user ID. Note that the actual shared user data is not available here and
@@ -155,21 +153,21 @@
* @return the shared user this package is a part of, or null if it's not part of a shared user.
*/
@Nullable
- default Integer getSharedUserId() { return null; }
+ Integer getSharedUserId();
@NonNull
- default SigningInfo getSigningInfo() { return null; }
+ SigningInfo getSigningInfo();
/**
* Valid users for this package, for use with {@link #getUserState(int)}.
*/
- default int[] getUserIds() { return null; }
+ int[] getUserIds();
/**
* Retrieves per-user state for this package. Acceptable user IDs are in {@link #getUserIds()}.
*/
@Nullable
- default PackageUserState getUserState(@UserIdInt int userId) { return null; }
+ PackageUserState getUserState(@UserIdInt int userId);
/**
* The actual files resolved for each shared library.
@@ -177,42 +175,42 @@
* @see R.styleable#AndroidManifestUsesLibrary
*/
@NonNull
- default List<String> getUsesLibraryFiles() { return null; }
+ List<String> getUsesLibraryFiles();
/**
* @see R.styleable#AndroidManifestUsesLibrary
*/
@NonNull
- default List<SharedLibraryInfo> getUsesLibraryInfos() { return null; }
+ List<SharedLibraryInfo> getUsesLibraryInfos();
/**
* @see R.styleable#AndroidManifestUsesStaticLibrary
*/
@NonNull
- default String[] getUsesStaticLibraries() { return null; }
+ String[] getUsesStaticLibraries();
/**
* @see R.styleable#AndroidManifestUsesStaticLibrary_version
*/
@NonNull
- default long[] getUsesStaticLibrariesVersions() { return null; }
+ long[] getUsesStaticLibrariesVersions();
/**
* @see AndroidPackageApi#getVolumeUuid()
*/
@Nullable
- default String getVolumeUuid() { return null; }
+ String getVolumeUuid();
/**
* @see AndroidPackageApi#isExternalStorage()
*/
- default boolean isExternalStorage() { return false; }
+ boolean isExternalStorage();
/**
* Whether a package was installed --force-queryable such that it is always queryable by any
* package, regardless of their manifest content.
*/
- default boolean isForceQueryableOverride() { return false; }
+ boolean isForceQueryableOverride();
/**
* Whether a package is treated as hidden until it is installed for a user.
@@ -220,58 +218,58 @@
* @see PackageManager#MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
* @see PackageManager#setSystemAppState
*/
- default boolean isHiddenUntilInstalled() { return false; }
+ boolean isHiddenUntilInstalled();
- default boolean isInstallPermissionsFixed() { return false; }
+ boolean isInstallPermissionsFixed();
/**
* @see AndroidPackageApi#isOdm()
*/
- default boolean isOdm() { return false; }
+ boolean isOdm();
/**
* @see AndroidPackageApi#isOem()
*/
- default boolean isOem() { return false; }
+ boolean isOem();
/**
* @see AndroidPackageApi#isPrivileged()
*/
- default boolean isPrivileged() { return false; }
+ boolean isPrivileged();
/**
* @see AndroidPackageApi#isProduct()
*/
- default boolean isProduct() { return false; }
+ boolean isProduct();
/**
* @see ApplicationInfo#PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER
*/
- default boolean isRequiredForSystemUser() { return false; }
+ boolean isRequiredForSystemUser();
/**
* @see AndroidPackageApi#isSystem()
*/
- default boolean isSystem() { return false; }
+ boolean isSystem();
/**
* @see AndroidPackageApi#isSystemExt()
*/
- default boolean isSystemExt() { return false; }
+ boolean isSystemExt();
/**
* Whether or not an update is available. Ostensibly only for instant apps.
*/
- default boolean isUpdateAvailable() { return false; }
+ boolean isUpdateAvailable();
/**
* Whether this app is on the /data partition having been upgraded from a preinstalled app on a
* system partition.
*/
- default boolean isUpdatedSystemApp() { return false; }
+ boolean isUpdatedSystemApp();
/**
* @see AndroidPackageApi#isVendor()
*/
- default boolean isVendor() { return false; }
+ boolean isVendor();
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
index 6cd55c1..4c593d9 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
@@ -477,7 +477,7 @@
}
@DataClass.Generated(
- time = 1630602933994L,
+ time = 1630604430207L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\npublic static android.content.pm.pkg.PackageUserState copy(android.content.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\nclass UserStateImpl extends java.lang.Object implements [android.content.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@@ -637,7 +637,7 @@
}
@DataClass.Generated(
- time = 1630602934025L,
+ time = 1630604430248L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackageApi mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final long mFirstInstallTime\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.Integer mSharedUserId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.Nullable java.lang.String mSeInfoOverride\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull int[] mUserIds\nprivate final @android.annotation.NonNull android.util.SparseArray<android.content.pm.pkg.PackageUserState> mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.PackageSetting)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @android.annotation.Nullable @java.lang.Override android.content.pm.pkg.PackageUserState getUserState(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 8dc02b7..f0f825a 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -949,7 +949,8 @@
} else {
pkgState = mSettings.removeRestoredState(pkgName);
if (pkgState != null && !Objects.equals(pkgState.getBackupSignatureHash(),
- PackageUtils.computeSignaturesSha256Digest(newPkgSetting.getSignatures()))) {
+ PackageUtils.computeSignaturesSha256Digest(
+ newPkgSetting.getSigningDetails().getSignatures()))) {
// If restoring and the signatures don't match, drop the state
pkgState = null;
}
@@ -1072,7 +1073,7 @@
}
return PackageUtils.computeSignaturesSha256Digest(
- pkgSetting.getSignatures());
+ pkgSetting.getSigningDetails().getSignatures());
};
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3185a28..811a434 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -504,7 +504,6 @@
private boolean mKeyguardOccludedChanged;
private ActivityTaskManagerInternal.SleepTokenAcquirer mScreenOffSleepTokenAcquirer;
- volatile boolean mKeyguardOccluded;
Intent mHomeIntent;
Intent mCarDockIntent;
Intent mDeskDockIntent;
@@ -2399,7 +2398,7 @@
// the keyguard is being hidden. This is okay because starting windows never show
// secret information.
// TODO(b/113840485): Occluded may not only happen on default display
- if (displayId == DEFAULT_DISPLAY && mKeyguardOccluded) {
+ if (displayId == DEFAULT_DISPLAY && isKeyguardOccluded()) {
windowFlags |= FLAG_SHOW_WHEN_LOCKED;
}
}
@@ -3217,7 +3216,7 @@
return;
}
- if (!mKeyguardOccluded && mKeyguardDelegate.isInputRestricted()) {
+ if (!isKeyguardOccluded() && mKeyguardDelegate.isInputRestricted()) {
// when in keyguard restricted mode, must first verify unlock
// before launching home
mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
@@ -3268,42 +3267,38 @@
@Override
public void setKeyguardCandidateLw(WindowState win) {
mKeyguardCandidate = win;
- setKeyguardOccludedLw(mKeyguardOccluded, true /* force */);
+ setKeyguardOccludedLw(isKeyguardOccluded(), true /* force */);
}
/**
* Updates the occluded state of the Keyguard.
*
+ * @param isOccluded Whether the Keyguard is occluded by another window.
+ * @param force notify the occluded status to KeyguardService and update flags even though
+ * occlude status doesn't change.
* @return Whether the flags have changed and we have to redo the layout.
*/
private boolean setKeyguardOccludedLw(boolean isOccluded, boolean force) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
- final boolean wasOccluded = mKeyguardOccluded;
- final boolean showing = mKeyguardDelegate.isShowing();
- final boolean changed = wasOccluded != isOccluded || force;
- if (!isOccluded && changed && showing) {
- mKeyguardOccluded = false;
- mKeyguardDelegate.setOccluded(false, true /* animate */);
- if (mKeyguardCandidate != null) {
- if (!mKeyguardDelegate.hasLockscreenWallpaper()) {
- mKeyguardCandidate.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
- }
- }
- return true;
- } else if (isOccluded && changed && showing) {
- mKeyguardOccluded = true;
- mKeyguardDelegate.setOccluded(true, false /* animate */);
- if (mKeyguardCandidate != null) {
- mKeyguardCandidate.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER;
- }
- return true;
- } else if (changed) {
- mKeyguardOccluded = isOccluded;
- mKeyguardDelegate.setOccluded(isOccluded, false /* animate */);
- return false;
- } else {
+ if (isKeyguardOccluded() == isOccluded && !force) {
return false;
}
+
+ final boolean showing = mKeyguardDelegate.isShowing();
+ final boolean animate = showing && !isOccluded;
+ mKeyguardDelegate.setOccluded(isOccluded, animate);
+
+ if (!showing) {
+ return false;
+ }
+ if (mKeyguardCandidate != null) {
+ if (isOccluded) {
+ mKeyguardCandidate.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER;
+ } else if (!mKeyguardDelegate.hasLockscreenWallpaper()) {
+ mKeyguardCandidate.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
+ }
+ }
+ return true;
}
/** {@inheritDoc} */
@@ -4603,7 +4598,7 @@
@Override
public boolean isKeyguardShowingAndNotOccluded() {
if (mKeyguardDelegate == null) return false;
- return mKeyguardDelegate.isShowing() && !mKeyguardOccluded;
+ return mKeyguardDelegate.isShowing() && !isKeyguardOccluded();
}
@Override
@@ -4629,7 +4624,7 @@
@Override
public boolean isKeyguardOccluded() {
if (mKeyguardDelegate == null) return false;
- return mKeyguardOccluded;
+ return mKeyguardDelegate.isOccluded();
}
/** {@inheritDoc} */
@@ -5315,7 +5310,7 @@
proto.write(KEYGUARD_DRAW_COMPLETE, mDefaultDisplayPolicy.isKeyguardDrawComplete());
proto.write(WINDOW_MANAGER_DRAW_COMPLETE,
mDefaultDisplayPolicy.isWindowManagerDrawComplete());
- proto.write(KEYGUARD_OCCLUDED, mKeyguardOccluded);
+ proto.write(KEYGUARD_OCCLUDED, isKeyguardOccluded());
proto.write(KEYGUARD_OCCLUDED_CHANGED, mKeyguardOccludedChanged);
proto.write(KEYGUARD_OCCLUDED_PENDING, mPendingKeyguardOccluded);
if (mKeyguardDelegate != null) {
@@ -5400,7 +5395,7 @@
final int key = mDisplayHomeButtonHandlers.keyAt(i);
pw.println(mDisplayHomeButtonHandlers.get(key));
}
- pw.print(prefix); pw.print("mKeyguardOccluded="); pw.print(mKeyguardOccluded);
+ pw.print(prefix); pw.print("mKeyguardOccluded="); pw.print(isKeyguardOccluded());
pw.print(" mKeyguardOccludedChanged="); pw.print(mKeyguardOccludedChanged);
pw.print(" mPendingKeyguardOccluded="); pw.println(mPendingKeyguardOccluded);
pw.print(prefix); pw.print("mAllowLockscreenWhenOnDisplays=");
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 0535af5..d190678 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -67,7 +67,7 @@
boolean showing;
boolean showingAndNotOccluded;
boolean inputRestricted;
- boolean occluded;
+ volatile boolean occluded;
boolean secure;
boolean dreaming;
boolean systemIsReady;
@@ -272,6 +272,10 @@
mKeyguardState.occluded = isOccluded;
}
+ public boolean isOccluded() {
+ return mKeyguardState.occluded;
+ }
+
public void dismiss(IKeyguardDismissCallback callback, CharSequence message) {
if (mKeyguardService != null) {
mKeyguardService.dismiss(callback, message);
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
index 5fe06ee..227424e 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
@@ -26,8 +26,6 @@
import android.util.Log;
import java.util.Objects;
-import java.util.Timer;
-import java.util.TimerTask;
/**
* An {@link ISoundTriggerHal} decorator that would enforce deadlines on all calls and reboot the
@@ -38,11 +36,11 @@
private static final String TAG = "SoundTriggerHalWatchdog";
private final @NonNull ISoundTriggerHal mUnderlying;
- private final @NonNull Timer mTimer;
+ private final @NonNull UptimeTimer mTimer;
public SoundTriggerHalWatchdog(@NonNull ISoundTriggerHal underlying) {
mUnderlying = Objects.requireNonNull(underlying);
- mTimer = new Timer("SoundTriggerHalWatchdog");
+ mTimer = new UptimeTimer("SoundTriggerHalWatchdog");
}
@Override
@@ -155,21 +153,16 @@
}
private class Watchdog implements AutoCloseable {
- private final @NonNull
- TimerTask mTask;
+ private final @NonNull UptimeTimer.Task mTask;
// This exception is used merely for capturing a stack trace at the time of creation.
private final @NonNull
Exception mException = new Exception();
Watchdog() {
- mTask = new TimerTask() {
- @Override
- public void run() {
- Log.e(TAG, "HAL deadline expired. Rebooting.", mException);
- reboot();
- }
- };
- mTimer.schedule(mTask, TIMEOUT_MS);
+ mTask = mTimer.createTask(() -> {
+ Log.e(TAG, "HAL deadline expired. Rebooting.", mException);
+ reboot();
+ }, TIMEOUT_MS);
}
@Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/UptimeTimer.java b/services/core/java/com/android/server/soundtrigger_middleware/UptimeTimer.java
new file mode 100644
index 0000000..bfcc7d8
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/UptimeTimer.java
@@ -0,0 +1,90 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A simple timer, similar to java.util.Timer, but using the "uptime clock".
+ *
+ * Example usage:
+ * UptimeTimer timer = new UptimeTimer("TimerThread");
+ * UptimeTimer.Task task = timer.createTask(() -> { ... }, 100);
+ * ...
+ * // optionally, some time later:
+ * task.cancel();
+ */
+class UptimeTimer {
+ private Handler mHandler = null;
+
+ interface Task {
+ void cancel();
+ }
+
+ UptimeTimer(String threadName) {
+ new Thread(this::threadFunc, threadName).start();
+ synchronized (this) {
+ while (mHandler == null) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ Task createTask(@NonNull Runnable runnable, long uptimeMs) {
+ TaskImpl task = new TaskImpl(runnable);
+ mHandler.postDelayed(task, uptimeMs);
+ return task;
+ }
+
+ private void threadFunc() {
+ Looper.prepare();
+ synchronized (this) {
+ mHandler = new Handler(Looper.myLooper());
+ notifyAll();
+ }
+ Looper.loop();
+ }
+
+ private static class TaskImpl implements Task, Runnable {
+ private AtomicReference<Runnable> mRunnable = new AtomicReference<>();
+
+ TaskImpl(@NonNull Runnable runnable) {
+ mRunnable.set(runnable);
+ }
+
+ @Override
+ public void cancel() {
+ mRunnable.set(null);
+ }
+
+ @Override
+ public void run() {
+ Runnable runnable = mRunnable.get();
+ if (runnable != null) {
+ runnable.run();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 61770ea..106cff1 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -67,6 +67,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.AppOpsManager.HistoricalOp;
@@ -82,6 +83,7 @@
import android.bluetooth.BluetoothActivityEnergyInfo;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.UidTraffic;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -734,6 +736,10 @@
case FrameworkStatsLog.RKP_ERROR_STATS:
case FrameworkStatsLog.KEYSTORE2_CRASH_STATS:
return pullKeystoreAtoms(atomTag, data);
+ case FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS:
+ return pullAccessibilityShortcutStatsLocked(atomTag, data);
+ case FrameworkStatsLog.ACCESSIBILITY_FLOATING_MENU_STATS:
+ return pullAccessibilityFloatingMenuStatsLocked(atomTag, data);
default:
throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
}
@@ -930,6 +936,8 @@
registerKeystoreKeyOperationWithGeneralInfo();
registerRkpErrorStats();
registerKeystoreCrashStats();
+ registerAccessibilityShortcutStats();
+ registerAccessibilityFloatingMenuStats();
}
private void initAndRegisterNetworkStatsPullers() {
@@ -4150,6 +4158,26 @@
mStatsCallbackImpl);
}
+ private void registerAccessibilityShortcutStats() {
+ int tagId = FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_STATS;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
+
+ private void registerAccessibilityFloatingMenuStats() {
+ int tagId = FrameworkStatsLog.ACCESSIBILITY_FLOATING_MENU_STATS;
+ mStatsManager.setPullAtomCallback(
+ tagId,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ mStatsCallbackImpl
+ );
+ }
+
int parseKeystoreStorageStats(KeystoreAtom[] atoms, List<StatsEvent> pulledData) {
for (KeystoreAtom atomWrapper : atoms) {
if (atomWrapper.payload.getTag() != KeystoreAtomPayload.storageStats) {
@@ -4341,6 +4369,144 @@
}
}
+ int pullAccessibilityShortcutStatsLocked(int atomTag, List<StatsEvent> pulledData) {
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ if (userManager == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final ContentResolver resolver = mContext.getContentResolver();
+ final int hardware_shortcut_type =
+ FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__VOLUME_KEY;
+ final int triple_tap_shortcut =
+ FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__TRIPLE_TAP;
+ for (UserInfo userInfo : userManager.getUsers()) {
+ final int userId = userInfo.getUserHandle().getIdentifier();
+
+ if (isAccessibilityShortcutUser(mContext, userId)) {
+ final int software_shortcut_type = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 0, userId);
+ final String software_shortcut_list = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
+ final int software_shortcut_service_num = countAccessibilityServices(
+ software_shortcut_list);
+
+ final String hardware_shortcut_list = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId);
+ final int hardware_shortcut_service_num = countAccessibilityServices(
+ hardware_shortcut_list);
+
+ // only allow magnification to use it for now
+ final int triple_tap_service_num = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId);
+
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(atomTag,
+ software_shortcut_type, software_shortcut_service_num,
+ hardware_shortcut_type, hardware_shortcut_service_num,
+ triple_tap_shortcut, triple_tap_service_num));
+ }
+ }
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "pulling accessibility shortcuts stats failed at getUsers", e);
+ return StatsManager.PULL_SKIP;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ int pullAccessibilityFloatingMenuStatsLocked(int atomTag, List<StatsEvent> pulledData) {
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ if (userManager == null) {
+ return StatsManager.PULL_SKIP;
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ final ContentResolver resolver = mContext.getContentResolver();
+ final int defaultSize = 0;
+ final int defaultIconType = 0;
+ final int defaultFadeEnabled = 1;
+ final float defaultOpacity = 0.55f;
+
+ for (UserInfo userInfo : userManager.getUsers()) {
+ final int userId = userInfo.getUserHandle().getIdentifier();
+
+ if (isAccessibilityFloatingMenuUser(mContext, userId)) {
+ final int size = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE, defaultSize, userId);
+ final int type = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
+ defaultIconType, userId);
+ final boolean fadeEnabled = (Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
+ defaultFadeEnabled, userId)) == 1;
+ final float opacity = Settings.Secure.getFloatForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY,
+ defaultOpacity, userId);
+
+ pulledData.add(
+ FrameworkStatsLog.buildStatsEvent(atomTag, size, type, fadeEnabled,
+ opacity));
+ }
+ }
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "pulling accessibility floating menu stats failed at getUsers", e);
+ return StatsManager.PULL_SKIP;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ return StatsManager.PULL_SUCCESS;
+ }
+
+ /**
+ * Counts how many accessibility services (including features) there are in the colon-separated
+ * string list.
+ *
+ * @param semicolonList colon-separated string, it should be
+ * {@link Settings.Secure#ACCESSIBILITY_BUTTON_TARGETS} or
+ * {@link Settings.Secure#ACCESSIBILITY_SHORTCUT_TARGET_SERVICE}.
+ * @return The number of accessibility services
+ */
+ private int countAccessibilityServices(String semicolonList) {
+ if (TextUtils.isEmpty(semicolonList)) {
+ return 0;
+ }
+ final int semiColonNums = (int) semicolonList.chars().filter(ch -> ch == ':').count();
+ return TextUtils.isEmpty(semicolonList) ? 0 : semiColonNums + 1;
+ }
+
+ private boolean isAccessibilityShortcutUser(Context context, @UserIdInt int userId) {
+ final ContentResolver resolver = context.getContentResolver();
+
+ final String software_shortcut_list = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
+ final String hardware_shortcut_list = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, userId);
+ final boolean hardware_shortcut_dialog_shown = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId) == 1;
+ final boolean software_shortcut_enabled = !TextUtils.isEmpty(software_shortcut_list);
+ final boolean hardware_shortcut_enabled =
+ hardware_shortcut_dialog_shown && !TextUtils.isEmpty(hardware_shortcut_list);
+ final boolean triple_tap_shortcut_enabled = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0, userId) == 1;
+
+ return software_shortcut_enabled || hardware_shortcut_enabled
+ || triple_tap_shortcut_enabled;
+ }
+
+ private boolean isAccessibilityFloatingMenuUser(Context context, @UserIdInt int userId) {
+ final ContentResolver resolver = context.getContentResolver();
+ final int mode = Settings.Secure.getIntForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 0, userId);
+ final String software_string = Settings.Secure.getStringForUser(resolver,
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
+
+ return (mode == Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU)
+ && !TextUtils.isEmpty(software_string);
+ }
+
// Thermal event received from vendor thermal management subsystem
private static final class ThermalEventListener extends IThermalEventListener.Stub {
@Override
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 0b34eb8..9faf7a9 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -260,16 +260,11 @@
}
/**
- * Restarts all sessions for {@code userId}.
- *
- * Does nothing if {@link #shouldHandle} is {@code false}
- *
- * This call blocks and waits for all sessions to be started, however any failures when starting
- * a session will be ignored.
+ * Makes sure we initialize the ExternalStorageService component.
*/
public void onUnlockUser(int userId) throws ExternalStorageServiceException {
Slog.i(TAG, "On user unlock " + userId);
- if (shouldHandle(null) && userId == 0) {
+ if (userId == 0) {
initExternalStorageServiceComponent();
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 6222808..e190b1e 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -17,10 +17,13 @@
package com.android.server.wallpaper;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.QUERY_ALL_PACKAGES;
+import static android.Manifest.permission.READ_WALLPAPER_INTERNAL;
import static android.app.WallpaperManager.COMMAND_REAPPLY;
import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.ParcelFileDescriptor.MODE_CREATE;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
@@ -2064,7 +2067,7 @@
private boolean hasCrossUserPermission() {
final int interactPermission =
mContext.checkCallingPermission(INTERACT_ACROSS_USERS_FULL);
- return interactPermission == PackageManager.PERMISSION_GRANTED;
+ return interactPermission == PERMISSION_GRANTED;
}
@Override
@@ -2255,9 +2258,8 @@
@Override
public ParcelFileDescriptor getWallpaperWithFeature(String callingPkg, String callingFeatureId,
IWallpaperManagerCallback cb, final int which, Bundle outParams, int wallpaperUserId) {
- final int hasPrivilege = mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.READ_WALLPAPER_INTERNAL);
- if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
+ final boolean hasPrivilege = hasPermission(READ_WALLPAPER_INTERNAL);
+ if (!hasPrivilege) {
mContext.getSystemService(StorageManager.class).checkPermissionReadImages(true,
Binder.getCallingPid(), Binder.getCallingUid(), callingPkg, callingFeatureId);
}
@@ -2301,17 +2303,26 @@
}
}
+ private boolean hasPermission(String permission) {
+ return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
+ }
+
@Override
public WallpaperInfo getWallpaperInfo(int userId) {
- userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
- Binder.getCallingUid(), userId, false, true, "getWallpaperInfo", null);
- synchronized (mLock) {
- WallpaperData wallpaper = mWallpaperMap.get(userId);
- if (wallpaper != null && wallpaper.connection != null) {
- return wallpaper.connection.mInfo;
+ final boolean allow =
+ hasPermission(READ_WALLPAPER_INTERNAL) || hasPermission(QUERY_ALL_PACKAGES);
+ if (allow) {
+ userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, false, true, "getWallpaperInfo", null);
+ synchronized (mLock) {
+ WallpaperData wallpaper = mWallpaperMap.get(userId);
+ if (wallpaper != null && wallpaper.connection != null) {
+ return wallpaper.connection.mInfo;
+ }
}
- return null;
}
+
+ return null;
}
@Override
@@ -2935,7 +2946,7 @@
final int hasPrivilege = mIPackageManager.checkPermission(
android.Manifest.permission.AMBIENT_WALLPAPER, wi.getPackageName(),
serviceUserId);
- if (hasPrivilege != PackageManager.PERMISSION_GRANTED) {
+ if (hasPrivilege != PERMISSION_GRANTED) {
String msg = "Selected service does not have "
+ android.Manifest.permission.AMBIENT_WALLPAPER
+ ": " + componentName;
@@ -3056,7 +3067,7 @@
}
private void checkPermission(String permission) {
- if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
+ if (!hasPermission(permission)) {
throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
+ ", must have permission " + permission);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 195da18..c1fcf71 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1411,9 +1411,8 @@
this.task = newTask;
if (shouldStartChangeTransition(newParent, oldParent)) {
- // The new parent and old parent may be in different position. Need to offset the
- // animation surface to keep it in its original position.
- initializeChangeTransition(getBounds(), newParent.getBounds());
+ // Animate change transition on TaskFragment level to get the correct window crop.
+ newParent.initializeChangeTransition(getBounds(), getSurfaceControl());
}
super.onParentChanged(newParent, oldParent);
@@ -1482,7 +1481,7 @@
// The starting window should keep covering its task when the activity is
// reparented to a task fragment that may not fill the task bounds.
associateStartingDataWithTask();
- overrideConfigurationPropagation(mStartingWindow, task);
+ attachStartingSurfaceToAssociatedTask();
}
mImeInsetsFrozenUntilStartInput = false;
}
@@ -2383,13 +2382,20 @@
}
void attachStartingWindow(@NonNull WindowState startingWindow) {
+ startingWindow.mStartingData = mStartingData;
mStartingWindow = startingWindow;
if (mStartingData != null && mStartingData.mAssociatedTask != null) {
- // Associate the configuration of starting window with the task.
- overrideConfigurationPropagation(startingWindow, mStartingData.mAssociatedTask);
+ attachStartingSurfaceToAssociatedTask();
}
}
+ private void attachStartingSurfaceToAssociatedTask() {
+ // Associate the configuration of starting window with the task.
+ overrideConfigurationPropagation(mStartingWindow, mStartingData.mAssociatedTask);
+ getSyncTransaction().reparent(mStartingWindow.mSurfaceControl,
+ mStartingData.mAssociatedTask.mSurfaceControl);
+ }
+
private void associateStartingDataWithTask() {
mStartingData.mAssociatedTask = task;
task.forAllActivities(r -> {
@@ -4946,11 +4952,10 @@
// dispatchTaskInfoChangedIfNeeded() right after ActivityRecord#setVisibility() can report
// the stale visible state, because the state will be updated after the app transition.
// So tries to report the actual visible state again where the state is changed.
- if (!mTaskSupervisor.inActivityVisibilityUpdate()) {
- final Task task = getOrganizedTask();
- if (task != null) {
- task.dispatchTaskInfoChangedIfNeeded(false /* force */);
- }
+ Task task = getOrganizedTask();
+ while (task != null) {
+ task.dispatchTaskInfoChangedIfNeeded(false /* force */);
+ task = task.getParent().asTask();
}
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"commitVisibility: %s: visible=%b mVisibleRequested=%b", this,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 0ba77d8..9db13ba 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -596,11 +596,56 @@
public abstract boolean isBaseOfLockedTask(String packageName);
/**
- * Create an interface to update configuration for an application.
+ * Creates an interface to update configuration for the calling application.
*/
public abstract PackageConfigurationUpdater createPackageConfigurationUpdater();
/**
+ * Creates an interface to update configuration for an arbitrary application specified by it's
+ * packageName and userId.
+ */
+ public abstract PackageConfigurationUpdater createPackageConfigurationUpdater(
+ String packageName, int userId);
+
+ /**
+ * Retrieves and returns the app-specific configuration for an arbitrary application specified
+ * by its packageName and userId. Returns null if no app-specific configuration has been set.
+ */
+ @Nullable
+ public abstract PackageConfig getApplicationConfig(String packageName,
+ int userId);
+
+ /**
+ * Holds app-specific configurations.
+ */
+ public static class PackageConfig {
+ /**
+ * nightMode for the application, null if app-specific nightMode is not set.
+ */
+ @Nullable
+ public final Integer mNightMode;
+
+ /**
+ * {@link LocaleList} for the application, null if app-specific locales are not set.
+ */
+ @Nullable
+ public final LocaleList mLocales;
+
+ PackageConfig(Integer nightMode, LocaleList locales) {
+ mNightMode = nightMode;
+ mLocales = locales;
+ }
+
+ /**
+ * Returns the string representation of the app-specific configuration.
+ */
+ @Override
+ public String toString() {
+ return "PackageConfig: nightMode " + mNightMode + " locales " + mLocales;
+ }
+ }
+
+ /**
* An interface to update configuration for an application, and will persist override
* configuration for this package.
*/
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 6f61221..41928fc 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6551,6 +6551,20 @@
}
@Override
+ public PackageConfigurationUpdater createPackageConfigurationUpdater(
+ String packageName , int userId) {
+ return new PackageConfigurationUpdaterImpl(packageName, userId,
+ ActivityTaskManagerService.this);
+ }
+
+ @Override
+ @Nullable
+ public ActivityTaskManagerInternal.PackageConfig getApplicationConfig(String packageName,
+ int userId) {
+ return mPackageConfigPersister.findPackageConfiguration(packageName, userId);
+ }
+
+ @Override
public boolean hasSystemAlertWindowPermission(int callingUid, int callingPid,
String callingPackage) {
return ActivityTaskManagerService.this.hasSystemAlertWindowPermission(callingUid,
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 145bdfd..11936b2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -841,6 +841,12 @@
logIfTransactionTooLarge(r.intent, r.getSavedState());
+ if (r.isEmbedded()) {
+ // Sending TaskFragmentInfo to client to ensure the info is updated before
+ // the activity creation.
+ mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent(
+ r.getOrganizedTaskFragment());
+ }
// Create activity launch transaction.
final ClientTransaction clientTransaction = ClientTransaction.obtain(
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index 892db9c..bf66107 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -31,6 +31,7 @@
import android.view.InputApplicationHandle;
import com.android.server.am.ActivityManagerService;
+import com.android.server.am.CriticalEventLog;
import com.android.server.wm.EmbeddedWindowController.EmbeddedWindow;
import java.io.File;
@@ -222,9 +223,10 @@
}
}
+ String criticalEvents = CriticalEventLog.getInstance().logLinesForAnrFile();
final File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
null /* processCpuTracker */, null /* lastPids */, nativePids,
- null /* logExceptionCreatingFile */);
+ null /* logExceptionCreatingFile */, "Pre-dump", criticalEvents);
if (tracesFile != null) {
tracesFile.renameTo(new File(tracesFile.getParent(), tracesFile.getName() + "_pre"));
}
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index a97ca6c..1a2bf9a 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -343,9 +343,6 @@
switch (changingType) {
case TYPE_TASK:
return TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
- case TYPE_ACTIVITY:
- // ActivityRecord is put in a change transition only when it is reparented
- // to an organized TaskFragment. See ActivityRecord#shouldStartChangeTransition.
case TYPE_TASK_FRAGMENT:
return TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
default:
@@ -533,40 +530,81 @@
*
* @return {@code true} if the transition is overridden.
*/
- @VisibleForTesting
- boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit,
+ private boolean overrideWithTaskFragmentRemoteAnimation(@TransitionOldType int transit,
ArraySet<Integer> activityTypes) {
final ArrayList<WindowContainer> allWindows = new ArrayList<>();
allWindows.addAll(mDisplayContent.mClosingApps);
allWindows.addAll(mDisplayContent.mOpeningApps);
allWindows.addAll(mDisplayContent.mChangingContainers);
- // Find the common TaskFragmentOrganizer of all windows.
- ITaskFragmentOrganizer organizer = null;
+ // It should only animated by the organizer if all windows are below the same leaf Task.
+ Task leafTask = null;
for (int i = allWindows.size() - 1; i >= 0; i--) {
final ActivityRecord r = getAppFromContainer(allWindows.get(i));
if (r == null) {
return false;
}
+ // The activity may be a child of embedded Task, but we want to find the owner Task.
+ // As a result, find the organized TaskFragment first.
final TaskFragment organizedTaskFragment = r.getOrganizedTaskFragment();
- final ITaskFragmentOrganizer curOrganizer = organizedTaskFragment != null
- ? organizedTaskFragment.getTaskFragmentOrganizer()
- : null;
- if (curOrganizer == null) {
- // All windows must below an organized TaskFragment.
+ // There are also cases where the Task contains non-embedded activity, such as launching
+ // split TaskFragments from a non-embedded activity.
+ // The hierarchy may looks like this:
+ // - Task
+ // - Activity
+ // - TaskFragment
+ // - Activity
+ // - TaskFragment
+ // - Activity
+ // We also want to have the organizer handle the transition for such case.
+ final Task task = organizedTaskFragment != null
+ ? organizedTaskFragment.getTask()
+ : r.getTask();
+ if (task == null) {
return false;
}
- if (organizer == null) {
- organizer = curOrganizer;
- } else if (!organizer.asBinder().equals(curOrganizer.asBinder())) {
- // They must be controlled by the same organizer.
+ // We don't want the organizer to handle transition of other non-embedded Task.
+ if (leafTask != null && leafTask != task) {
return false;
}
+ final ActivityRecord rootActivity = task.getRootActivity();
+ // We don't want the organizer to handle transition when the whole app is closing.
+ if (rootActivity == null) {
+ return false;
+ }
+ // We don't want the organizer to handle transition of non-embedded activity of other
+ // app.
+ if (r.getUid() != rootActivity.getUid() && !r.isEmbedded()) {
+ return false;
+ }
+ leafTask = task;
+ }
+ if (leafTask == null) {
+ return false;
}
- final RemoteAnimationDefinition definition = organizer != null
+ // We don't support remote animation for Task with multiple TaskFragmentOrganizers.
+ final ITaskFragmentOrganizer[] organizer = new ITaskFragmentOrganizer[1];
+ final boolean hasMultipleOrganizers = leafTask.forAllLeafTaskFragments(taskFragment -> {
+ final ITaskFragmentOrganizer tfOrganizer = taskFragment.getTaskFragmentOrganizer();
+ if (tfOrganizer == null) {
+ return false;
+ }
+ if (organizer[0] != null && !organizer[0].asBinder().equals(tfOrganizer.asBinder())) {
+ return true;
+ }
+ organizer[0] = tfOrganizer;
+ return false;
+ });
+ if (hasMultipleOrganizers) {
+ ProtoLog.e(WM_DEBUG_APP_TRANSITIONS, "We don't support remote animation for"
+ + " Task with multiple TaskFragmentOrganizers.");
+ return false;
+ }
+
+ final RemoteAnimationDefinition definition = organizer[0] != null
? mDisplayContent.mAtmService.mTaskFragmentOrganizerController
- .getRemoteAnimationDefinition(organizer)
+ .getRemoteAnimationDefinition(organizer[0])
: null;
final RemoteAnimationAdapter adapter = definition != null
? definition.getAdapter(transit, activityTypes)
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 322024d..5abf19c 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -3163,6 +3163,7 @@
pw.print(" mAllowLockscreenWhenOn="); pw.println(mAllowLockscreenWhenOn);
pw.print(prefix); pw.print("mRemoteInsetsControllerControlsSystemBars=");
pw.println(mDisplayContent.getInsetsPolicy().getRemoteInsetsControllerControlsSystemBars());
+ mSystemGestures.dump(pw, prefix);
pw.print(prefix); pw.println("Looper state:");
mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " ");
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 971bebd..225a6ea 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -442,7 +442,9 @@
return false;
}
- if (mDisplayContent.mFixedRotationTransitionListener
+ final RecentsAnimationController recentsAnimController =
+ mService.getRecentsAnimationController();
+ if (recentsAnimController != null && mDisplayContent.mFixedRotationTransitionListener
.isTopFixedOrientationRecentsAnimating()
// If screen is off or the device is going to sleep, then still allow to update.
&& mService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) {
@@ -450,6 +452,7 @@
// In order to ignore its requested orientation to avoid a sensor led rotation (e.g
// user rotating the device while the recents animation is running), we ignore
// rotation update while the animation is running.
+ recentsAnimController.setCheckRotationAfterCleanup();
return false;
}
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index f2e6abc..fa4d27c 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -47,6 +47,8 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS;
+import static java.lang.Integer.MAX_VALUE;
+
import android.annotation.Nullable;
import android.graphics.Rect;
import android.graphics.Region;
@@ -580,10 +582,11 @@
if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
if (recentsAnimationController.updateInputConsumerForApp(
mRecentsAnimationInputConsumer.mWindowHandle)) {
- final WindowState highestLayerWindow =
- recentsAnimationController.getHighestLayerWindow();
- if (highestLayerWindow != null) {
- mRecentsAnimationInputConsumer.show(mInputTransaction, highestLayerWindow);
+ final DisplayArea targetDA =
+ recentsAnimationController.getTargetAppDisplayArea();
+ if (targetDA != null) {
+ mRecentsAnimationInputConsumer.reparent(mInputTransaction, targetDA);
+ mRecentsAnimationInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
mAddRecentsAnimationInputConsumerHandle = false;
}
}
@@ -596,7 +599,7 @@
rootTask.getSurfaceControl());
// We set the layer to z=MAX-1 so that it's always on top.
mPipInputConsumer.reparent(mInputTransaction, rootTask);
- mPipInputConsumer.show(mInputTransaction, Integer.MAX_VALUE - 1);
+ mPipInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
mAddPipInputConsumerHandle = false;
}
}
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 45411a9..4b98013 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -36,6 +36,7 @@
import com.android.server.UiThread;
+import java.util.function.IntConsumer;
import java.util.function.Supplier;
/**
@@ -70,7 +71,7 @@
private final LetterboxSurface mFullWindowSurface = new LetterboxSurface("fullWindow");
private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
// Reachability gestures.
- private final Runnable mDoubleTapCallback;
+ private final IntConsumer mDoubleTapCallback;
/**
* Constructs a Letterbox.
@@ -84,7 +85,7 @@
Supplier<Boolean> hasWallpaperBackgroundSupplier,
Supplier<Integer> blurRadiusSupplier,
Supplier<Float> darkScrimAlphaSupplier,
- Runnable doubleTapCallback) {
+ IntConsumer doubleTapCallback) {
mSurfaceControlFactory = surfaceControlFactory;
mTransactionFactory = transactionFactory;
mAreCornersRounded = areCornersRounded;
@@ -262,7 +263,7 @@
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_UP) {
- mDoubleTapCallback.run();
+ mDoubleTapCallback.accept((int) e.getX());
return true;
}
return false;
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 72fbfcc..cbb473c 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -54,6 +54,27 @@
/** Using wallpaper as a background which can be blurred or dimmed with dark scrim. */
static final int LETTERBOX_BACKGROUND_WALLPAPER = 3;
+ /**
+ * Enum for Letterbox reachability position types.
+ *
+ * <p>Order from left to right is important since it's used in {@link
+ * #movePositionForReachabilityToNextRightStop} and {@link
+ * #movePositionForReachabilityToNextLeftStop}.
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({LETTERBOX_REACHABILITY_POSITION_LEFT, LETTERBOX_REACHABILITY_POSITION_CENTER,
+ LETTERBOX_REACHABILITY_POSITION_RIGHT})
+ @interface LetterboxReachabilityPosition {};
+
+ /** Letterboxed app window is aligned to the left side. */
+ static final int LETTERBOX_REACHABILITY_POSITION_LEFT = 0;
+
+ /** Letterboxed app window is positioned in the horizontal center. */
+ static final int LETTERBOX_REACHABILITY_POSITION_CENTER = 1;
+
+ /** Letterboxed app window is aligned to the right side. */
+ static final int LETTERBOX_REACHABILITY_POSITION_RIGHT = 2;
+
final Context mContext;
// Aspect ratio of letterbox for fixed orientation, values <=
@@ -85,25 +106,25 @@
// side of the screen and 1.0 to the right side.
private float mLetterboxHorizontalPositionMultiplier;
- // Default horizontal position of a center of the letterboxed app window when reachability is
- // enabled and an app is fullscreen in landscape device orientatio. 0 corresponds to the left
- // side of the screen and 1.0 to the right side.
- // It is used as a starting point for mLetterboxHorizontalMultiplierForReachability.
- private float mDefaultPositionMultiplierForReachability;
+ // Default horizontal position the letterboxed app window when reachability is enabled and
+ // an app is fullscreen in landscape device orientatio.
+ // It is used as a starting point for mLetterboxPositionForReachability.
+ @LetterboxReachabilityPosition
+ private int mDefaultPositionForReachability;
// Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape
// device orientation.
private boolean mIsReachabilityEnabled;
- // Horizontal position of a center of the letterboxed app window. 0 corresponds to
- // the left side of the screen and 1 to the right side. Keep it global to prevent
- // "jumps" when switching between letterboxed apps. It's updated to reposition the app
- // window in response to a double tap gesture (see LetterboxUiController#handleDoubleTap).
- // Used in LetterboxUiController#getHorizontalPositionMultiplier which is called from
+ // Horizontal position of a center of the letterboxed app window which is global to prevent
+ // "jumps" when switching between letterboxed apps. It's updated to reposition the app window
+ // in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in
+ // LetterboxUiController#getHorizontalPositionMultiplier which is called from
// ActivityRecord#updateResolvedBoundsHorizontalPosition.
// TODO(b/199426138): Global reachability setting causes a jump when resuming an app from
// Overview after changing position in another app.
- private volatile float mLetterboxHorizontalMultiplierForReachability;
+ @LetterboxReachabilityPosition
+ private volatile int mLetterboxPositionForReachability;
LetterboxConfiguration(Context systemUiContext) {
mContext = systemUiContext;
@@ -120,9 +141,8 @@
R.dimen.config_letterboxHorizontalPositionMultiplier);
mIsReachabilityEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsReachabilityEnabled);
- mDefaultPositionMultiplierForReachability = mContext.getResources().getFloat(
- R.dimen.config_letterboxDefaultPositionMultiplierForReachability);
- mLetterboxHorizontalMultiplierForReachability = mDefaultPositionMultiplierForReachability;
+ mDefaultPositionForReachability = readLetterboxReachabilityPositionFromConfig(mContext);
+ mLetterboxPositionForReachability = mDefaultPositionForReachability;
}
/**
@@ -395,58 +415,90 @@
}
/*
- * Gets default horizontal position of a center of the letterboxed app window when reachability
- * is enabled specified in {@link
- * R.dimen.config_letterboxDefaultPositionMultiplierForReachability} or via an ADB command.
- * 0 corresponds to the left side of the screen and 1 to the right side. The returned value is
- * >= 0.0 and <= 1.0.
+ * Gets default horizontal position of the letterboxed app window when reachability is enabled.
+ * Specified in {@link R.integer.config_letterboxDefaultPositionForReachability} or via an ADB
+ * command.
*/
- float getDefaultPositionMultiplierForReachability() {
- return (mDefaultPositionMultiplierForReachability < 0.0f
- || mDefaultPositionMultiplierForReachability > 1.0f)
- // Default to a right position if invalid value is provided.
- ? 1.0f : mDefaultPositionMultiplierForReachability;
+ @LetterboxReachabilityPosition
+ int getDefaultPositionForReachability() {
+ return mDefaultPositionForReachability;
}
/**
- * Overrides default horizontal position of a center of the letterboxed app window when
- * reachability is enabled. If given value < 0.0 or > 1.0, then it and a value of {@link
- * R.dimen.config_letterboxDefaultPositionMultiplierForReachability} are ignored and the right
- * position (1.0) is used.
+ * Overrides default horizonal position of the letterboxed app window when reachability
+ * is enabled.
*/
- void setDefaultPositionMultiplierForReachability(float multiplier) {
- mDefaultPositionMultiplierForReachability = multiplier;
+ void setDefaultPositionForReachability(@LetterboxReachabilityPosition int position) {
+ mDefaultPositionForReachability = position;
}
/**
- * Resets default horizontal position of a center of the letterboxed app window when
- * reachability is enabled to {@link
- * R.dimen.config_letterboxDefaultPositionMultiplierForReachability}.
+ * Resets default horizontal position of the letterboxed app window when reachability is
+ * enabled to {@link R.integer.config_letterboxDefaultPositionForReachability}.
*/
- void resetDefaultPositionMultiplierForReachability() {
- mDefaultPositionMultiplierForReachability = mContext.getResources().getFloat(
- R.dimen.config_letterboxDefaultPositionMultiplierForReachability);
+ void resetDefaultPositionForReachability() {
+ mDefaultPositionForReachability = readLetterboxReachabilityPositionFromConfig(mContext);
+ }
+
+ @LetterboxReachabilityPosition
+ private static int readLetterboxReachabilityPositionFromConfig(Context context) {
+ int position = context.getResources().getInteger(
+ R.integer.config_letterboxDefaultPositionForReachability);
+ return position == LETTERBOX_REACHABILITY_POSITION_LEFT
+ || position == LETTERBOX_REACHABILITY_POSITION_CENTER
+ || position == LETTERBOX_REACHABILITY_POSITION_RIGHT
+ ? position : LETTERBOX_REACHABILITY_POSITION_CENTER;
}
/*
* Gets horizontal position of a center of the letterboxed app window when reachability
* is enabled specified. 0 corresponds to the left side of the screen and 1 to the right side.
*
- * <p>The position multiplier is changed to a symmetrical value computed as (1 - current
- * multiplier) after each double tap in the letterbox area.
+ * <p>The position multiplier is changed after each double tap in the letterbox area.
*/
float getHorizontalMultiplierForReachability() {
- return mLetterboxHorizontalMultiplierForReachability;
+ switch (mLetterboxPositionForReachability) {
+ case LETTERBOX_REACHABILITY_POSITION_LEFT:
+ return 0.0f;
+ case LETTERBOX_REACHABILITY_POSITION_CENTER:
+ return 0.5f;
+ case LETTERBOX_REACHABILITY_POSITION_RIGHT:
+ return 1.0f;
+ default:
+ throw new AssertionError(
+ "Unexpected letterbox position type: " + mLetterboxPositionForReachability);
+ }
+ }
+
+ /** Returns a string representing the given {@link LetterboxReachabilityPosition}. */
+ static String letterboxReachabilityPositionToString(
+ @LetterboxReachabilityPosition int position) {
+ switch (position) {
+ case LETTERBOX_REACHABILITY_POSITION_LEFT:
+ return "LETTERBOX_REACHABILITY_POSITION_LEFT";
+ case LETTERBOX_REACHABILITY_POSITION_CENTER:
+ return "LETTERBOX_REACHABILITY_POSITION_CENTER";
+ case LETTERBOX_REACHABILITY_POSITION_RIGHT:
+ return "LETTERBOX_REACHABILITY_POSITION_RIGHT";
+ default:
+ throw new AssertionError(
+ "Unexpected letterbox position type: " + position);
+ }
}
/**
- * Changes horizontal position of a center of the letterboxed app window to the opposite
- * (1 - current multiplier) when reachability is enabled specified. 0 corresponds to the left
- * side of the screen and 1 to the right side.
+ * Changes letterbox position for reachability to the next available one on the right side.
*/
- void flipHorizontalMultiplierForReachability() {
- mLetterboxHorizontalMultiplierForReachability =
- 1.0f - mLetterboxHorizontalMultiplierForReachability;
+ void movePositionForReachabilityToNextRightStop() {
+ mLetterboxPositionForReachability = Math.min(
+ mLetterboxPositionForReachability + 1, LETTERBOX_REACHABILITY_POSITION_RIGHT);
+ }
+
+ /**
+ * Changes letterbox position for reachability to the next available one on the left side.
+ */
+ void movePositionForReachabilityToNextLeftStop() {
+ mLetterboxPositionForReachability = Math.max(mLetterboxPositionForReachability - 1, 0);
}
}
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index cf2afc9..7d07357 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -65,6 +65,10 @@
private final LetterboxConfiguration mLetterboxConfiguration;
private final ActivityRecord mActivityRecord;
+ // Taskbar expanded height. Used to determine whether to crop an app window to display rounded
+ // corners above the taskbar.
+ private float mExpandedTaskBarHeight;
+
private boolean mShowWallpaperForLetterboxBackground;
@Nullable
@@ -76,6 +80,8 @@
// is created in its constructor. It shouldn't be used in this constructor but it's safe
// to use it after since controller is only used in ActivityRecord.
mActivityRecord = activityRecord;
+ mExpandedTaskBarHeight =
+ getResources().getDimensionPixelSize(R.dimen.taskbar_frame_height);
}
/** Cleans up {@link Letterbox} if it exists.*/
@@ -204,12 +210,23 @@
return mActivityRecord.mWmService.mContext.getResources();
}
- private void handleDoubleTap() {
+ private void handleDoubleTap(int x) {
if (!isReachabilityEnabled() || mActivityRecord.isInTransition()) {
return;
}
- mLetterboxConfiguration.flipHorizontalMultiplierForReachability();
+ if (mLetterbox.getInnerFrame().left <= x && mLetterbox.getInnerFrame().right >= x) {
+ // Only react to clicks at the sides of the letterboxed app window.
+ return;
+ }
+
+ if (mLetterbox.getInnerFrame().left > x) {
+ // Moving to the next stop on the left side of the app window: right > center > left.
+ mLetterboxConfiguration.movePositionForReachabilityToNextLeftStop();
+ } else if (mLetterbox.getInnerFrame().right < x) {
+ // Moving to the next stop on the right side of the app window: left > center > right.
+ mLetterboxConfiguration.movePositionForReachabilityToNextRightStop();
+ }
// TODO(197549949): Add animation for transition.
mActivityRecord.recomputeConfiguration();
@@ -314,12 +331,27 @@
final InsetsSource taskbarInsetsSource =
insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
- Rect cropBounds = new Rect(mActivityRecord.getBounds());
- // Activity bounds are in screen coordinates while (0,0) for activity's surface control
- // is at the top left corner of an app window so offsetting bounds accordingly.
- cropBounds.offsetTo(0, 0);
- // Rounded cornerners should be displayed above the taskbar.
- cropBounds.bottom = Math.min(cropBounds.bottom, taskbarInsetsSource.getFrame().top);
+ Rect cropBounds = null;
+
+ // Rounded corners should be displayed above the taskbar. When taskbar is hidden,
+ // an insets frame is equal to a navigation bar which shouldn't affect position of
+ // rounded corners since apps are expected to handle navigation bar inset.
+ // This condition checks whether the taskbar is visible.
+ if (taskbarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) {
+ cropBounds = new Rect(mActivityRecord.getBounds());
+ // Activity bounds are in screen coordinates while (0,0) for activity's surface
+ // control is at the top left corner of an app window so offsetting bounds
+ // accordingly.
+ cropBounds.offsetTo(0, 0);
+ // Rounded cornerners should be displayed above the taskbar.
+ cropBounds.bottom =
+ Math.min(cropBounds.bottom, taskbarInsetsSource.getFrame().top);
+ if (mActivityRecord.inSizeCompatMode()
+ && mActivityRecord.getSizeCompatScale() < 1.0f) {
+ cropBounds.scale(1.0f / mActivityRecord.getSizeCompatScale());
+ }
+ }
+
transaction
.setWindowCrop(windowSurface, cropBounds)
.setCornerRadius(windowSurface, getRoundedCorners(insetsState));
diff --git a/services/core/java/com/android/server/wm/PackageConfigPersister.java b/services/core/java/com/android/server/wm/PackageConfigPersister.java
index 52eea4d..081a53e 100644
--- a/services/core/java/com/android/server/wm/PackageConfigPersister.java
+++ b/services/core/java/com/android/server/wm/PackageConfigPersister.java
@@ -272,6 +272,24 @@
}
}
+ /**
+ * Retrieves and returns application configuration from persisted records if it exists, else
+ * returns null.
+ */
+ ActivityTaskManagerInternal.PackageConfig findPackageConfiguration(String packageName,
+ int userId) {
+ synchronized (mLock) {
+ PackageConfigRecord packageConfigRecord = findRecord(mModified, packageName, userId);
+ if (packageConfigRecord == null) {
+ Slog.w(TAG, "App-specific configuration not found for packageName: " + packageName
+ + " and userId: " + userId);
+ return null;
+ }
+ return new ActivityTaskManagerInternal.PackageConfig(
+ packageConfigRecord.mNightMode, packageConfigRecord.mLocales);
+ }
+ }
+
// store a changed data so we don't need to get the process
static class PackageConfigRecord {
final String mName;
diff --git a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
index 96025054..8bbcf1f 100644
--- a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
+++ b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
@@ -16,26 +16,40 @@
package com.android.server.wm;
+import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.LocaleList;
+import android.util.ArraySet;
import android.util.Slog;
+import java.util.Optional;
+
/**
* An implementation of {@link ActivityTaskManagerInternal.PackageConfigurationUpdater}.
*/
final class PackageConfigurationUpdaterImpl implements
ActivityTaskManagerInternal.PackageConfigurationUpdater {
private static final String TAG = "PackageConfigurationUpdaterImpl";
- private final int mPid;
+ private final Optional<Integer> mPid;
private Integer mNightMode;
private LocaleList mLocales;
+ private String mPackageName;
+ private int mUserId;
private ActivityTaskManagerService mAtm;
PackageConfigurationUpdaterImpl(int pid, ActivityTaskManagerService atm) {
- mPid = pid;
+ mPid = Optional.of(pid);
mAtm = atm;
}
+ PackageConfigurationUpdaterImpl(String packageName, int userId,
+ ActivityTaskManagerService atm) {
+ mPackageName = packageName;
+ mUserId = userId;
+ mAtm = atm;
+ mPid = Optional.empty();
+ }
+
@Override
public ActivityTaskManagerInternal.PackageConfigurationUpdater setNightMode(int nightMode) {
synchronized (this) {
@@ -59,16 +73,29 @@
synchronized (mAtm.mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
- final WindowProcessController wpc = mAtm.mProcessMap.getProcess(mPid);
- if (wpc == null) {
- Slog.w(TAG, "Override application configuration: cannot find pid " + mPid);
- return;
+ final int uid;
+ if (mPid.isPresent()) {
+ WindowProcessController wpc = mAtm.mProcessMap.getProcess(mPid.get());
+ if (wpc == null) {
+ Slog.w(TAG, "commit: Override application configuration failed: "
+ + "cannot find pid " + mPid);
+ return;
+ }
+ uid = wpc.mUid;
+ mUserId = wpc.mUserId;
+ mPackageName = wpc.mInfo.packageName;
+ } else {
+ uid = mAtm.getPackageManagerInternalLocked().getPackageUid(mPackageName,
+ /* flags = */ PackageManager.MATCH_ALL, mUserId);
+ if (uid < 0) {
+ Slog.w(TAG, "commit: update of application configuration failed: "
+ + "userId or packageName not valid " + mUserId);
+ return;
+ }
}
- LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists(
- mLocales, mAtm.getGlobalConfiguration().getLocales());
- wpc.applyAppSpecificConfig(mNightMode, localesOverride);
- wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride);
- mAtm.mPackageConfigPersister.updateFromImpl(wpc.mName, wpc.mUserId, this);
+ updateConfig(uid, mPackageName);
+ mAtm.mPackageConfigPersister.updateFromImpl(mPackageName, mUserId, this);
+
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -76,6 +103,19 @@
}
}
+ private void updateConfig(int uid, String packageName) {
+ final ArraySet<WindowProcessController> processes = mAtm.mProcessMap.getProcesses(uid);
+ if (processes == null) return;
+ for (int i = processes.size() - 1; i >= 0; i--) {
+ final WindowProcessController wpc = processes.valueAt(i);
+ if (!wpc.mInfo.packageName.equals(packageName)) continue;
+ LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists(
+ mLocales, mAtm.getGlobalConfiguration().getLocales());
+ wpc.applyAppSpecificConfig(mNightMode, localesOverride);
+ wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride);
+ }
+ }
+
Integer getNightMode() {
return mNightMode;
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index ba85c98..a663c62 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -123,6 +123,7 @@
private final int mDisplayId;
private boolean mWillFinishToHome = false;
private final Runnable mFailsafeRunnable = this::onFailsafe;
+ private Runnable mCheckRotationAfterCleanup;
// The recents component app token that is shown behind the visibile tasks
private ActivityRecord mTargetActivityRecord;
@@ -921,6 +922,24 @@
}
/**
+ * If the display rotation change is ignored while recents animation is running, make sure that
+ * the pending rotation change will be applied after the animation finishes.
+ */
+ void setCheckRotationAfterCleanup() {
+ if (mCheckRotationAfterCleanup != null) return;
+ mCheckRotationAfterCleanup = () -> {
+ synchronized (mService.mGlobalLock) {
+ if (mDisplayContent.getDisplayRotation()
+ .updateRotationAndSendNewConfigIfChanged()) {
+ if (mTargetActivityRecord != null) {
+ mTargetActivityRecord.finishFixedRotationTransform();
+ }
+ }
+ }
+ };
+ }
+
+ /**
* @return Whether we should defer the cancel from a root task order change until the next app
* transition.
*/
@@ -1007,6 +1026,10 @@
if (mStatusBar != null) {
mStatusBar.onRecentsAnimationStateChanged(false /* running */);
}
+ if (mCheckRotationAfterCleanup != null) {
+ mService.mH.post(mCheckRotationAfterCleanup);
+ mCheckRotationAfterCleanup = null;
+ }
}
void scheduleFailsafe() {
@@ -1102,21 +1125,11 @@
return mTargetActivityRecord.findMainWindow();
}
- /**
- * Returns the window with the highest layer, or null if none is found.
- */
- public WindowState getHighestLayerWindow() {
- int highestLayer = Integer.MIN_VALUE;
- Task highestLayerTask = null;
- for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
- TaskAnimationAdapter adapter = mPendingAnimations.get(i);
- int layer = adapter.mTask.getPrefixOrderIndex();
- if (layer > highestLayer) {
- highestLayer = layer;
- highestLayerTask = adapter.mTask;
- }
+ DisplayArea getTargetAppDisplayArea() {
+ if (mTargetActivityRecord == null) {
+ return null;
}
- return highestLayerTask.getTopMostActivity().getTopChild();
+ return mTargetActivityRecord.getDisplayArea();
}
boolean isAnimatingTask(Task task) {
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index 9c4f6f5..89986ce 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -72,8 +72,11 @@
*
* @param startBounds The original bounds (on screen) of the surface we are snapshotting.
* @param relativePosition The related position of the snapshot surface to its parent.
+ * @param freezeTarget The surface to take snapshot from. If {@code null}, we will take a
+ * snapshot from the {@link #mAnimatable} surface.
*/
- void freeze(SurfaceControl.Transaction t, Rect startBounds, Point relativePosition) {
+ void freeze(SurfaceControl.Transaction t, Rect startBounds, Point relativePosition,
+ @Nullable SurfaceControl freezeTarget) {
mFreezeBounds.set(startBounds);
mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(),
@@ -82,7 +85,7 @@
mWmService.mTransactionFactory);
mAnimatable.onAnimationLeashCreated(t, mLeash);
- SurfaceControl freezeTarget = mAnimatable.getFreezeSnapshotTarget();
+ freezeTarget = freezeTarget != null ? freezeTarget : mAnimatable.getFreezeSnapshotTarget();
if (freezeTarget != null) {
SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = createSnapshotBuffer(
freezeTarget, startBounds);
diff --git a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
index 513b1b7..658f4ef 100644
--- a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
@@ -16,6 +16,12 @@
package com.android.server.wm;
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
+
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -33,6 +39,8 @@
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import android.widget.OverScroller;
+import java.io.PrintWriter;
+
/**
* Listens for system-wide input gestures, firing callbacks when detected.
* @hide
@@ -54,7 +62,8 @@
private final Context mContext;
private final Handler mHandler;
private int mDisplayCutoutTouchableRegionSize;
- private int mSwipeStartThreshold;
+ // The thresholds for each edge of the display
+ private final Rect mSwipeStartThreshold = new Rect();
private int mSwipeDistanceThreshold;
private final Callbacks mCallbacks;
private final int[] mDownPointerId = new int[MAX_TRACKED_POINTERS];
@@ -66,7 +75,6 @@
int screenHeight;
int screenWidth;
- private DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private int mDownPointers;
private boolean mSwipeFireable;
private boolean mDebugFireable;
@@ -88,27 +96,41 @@
void onConfigurationChanged() {
final Resources r = mContext.getResources();
+ final int defaultThreshold = r.getDimensionPixelSize(
+ com.android.internal.R.dimen.system_gestures_start_threshold);
+ mSwipeStartThreshold.set(defaultThreshold, defaultThreshold, defaultThreshold,
+ defaultThreshold);
+ mSwipeDistanceThreshold = defaultThreshold;
+
final Display display = DisplayManagerGlobal.getInstance()
.getRealDisplay(Display.DEFAULT_DISPLAY);
- display.getDisplayInfo(mTmpDisplayInfo);
- mSwipeStartThreshold = mTmpDisplayInfo.logicalWidth > mTmpDisplayInfo.logicalHeight
- ? r.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height_landscape)
- : r.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height_portrait);
-
final DisplayCutout displayCutout = display.getCutout();
if (displayCutout != null) {
- final Rect bounds = displayCutout.getBoundingRectTop();
- if (!bounds.isEmpty()) {
- // Expand swipe start threshold such that we can catch touches that just start below
- // the notch area
- mDisplayCutoutTouchableRegionSize = r.getDimensionPixelSize(
- com.android.internal.R.dimen.display_cutout_touchable_region_size);
- mSwipeStartThreshold += mDisplayCutoutTouchableRegionSize;
+ // Expand swipe start threshold such that we can catch touches that just start beyond
+ // the notch area
+ mDisplayCutoutTouchableRegionSize = r.getDimensionPixelSize(
+ com.android.internal.R.dimen.display_cutout_touchable_region_size);
+ final Rect[] bounds = displayCutout.getBoundingRectsAll();
+ if (bounds[BOUNDS_POSITION_LEFT] != null) {
+ mSwipeStartThreshold.left = Math.max(mSwipeStartThreshold.left,
+ bounds[BOUNDS_POSITION_LEFT].width() + mDisplayCutoutTouchableRegionSize);
+ }
+ if (bounds[BOUNDS_POSITION_TOP] != null) {
+ mSwipeStartThreshold.top = Math.max(mSwipeStartThreshold.top,
+ bounds[BOUNDS_POSITION_TOP].height() + mDisplayCutoutTouchableRegionSize);
+ }
+ if (bounds[BOUNDS_POSITION_RIGHT] != null) {
+ mSwipeStartThreshold.right = Math.max(mSwipeStartThreshold.right,
+ bounds[BOUNDS_POSITION_RIGHT].width() + mDisplayCutoutTouchableRegionSize);
+ }
+ if (bounds[BOUNDS_POSITION_BOTTOM] != null) {
+ mSwipeStartThreshold.bottom = Math.max(mSwipeStartThreshold.bottom,
+ bounds[BOUNDS_POSITION_BOTTOM].height()
+ + mDisplayCutoutTouchableRegionSize);
}
}
- mSwipeDistanceThreshold = mSwipeStartThreshold;
if (DEBUG) Slog.d(TAG, "mSwipeStartThreshold=" + mSwipeStartThreshold
- + " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold);
+ + " mSwipeDistanceThreshold=" + mSwipeDistanceThreshold);
}
private static <T> T checkNull(String name, T arg) {
@@ -275,22 +297,22 @@
final long elapsed = time - mDownTime[i];
if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i]
+ " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed);
- if (fromY <= mSwipeStartThreshold
+ if (fromY <= mSwipeStartThreshold.top
&& y > fromY + mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_TOP;
}
- if (fromY >= screenHeight - mSwipeStartThreshold
+ if (fromY >= screenHeight - mSwipeStartThreshold.bottom
&& y < fromY - mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_BOTTOM;
}
- if (fromX >= screenWidth - mSwipeStartThreshold
+ if (fromX >= screenWidth - mSwipeStartThreshold.right
&& x < fromX - mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_RIGHT;
}
- if (fromX <= mSwipeStartThreshold
+ if (fromX <= mSwipeStartThreshold.left
&& x > fromX + mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_LEFT;
@@ -298,6 +320,15 @@
return SWIPE_NONE;
}
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ final String inner = prefix + " ";
+ pw.println(prefix + TAG + ":");
+ pw.print(inner); pw.print("mDisplayCutoutTouchableRegionSize=");
+ pw.println(mDisplayCutoutTouchableRegionSize);
+ pw.print(inner); pw.print("mSwipeStartThreshold="); pw.println(mSwipeStartThreshold);
+ pw.print(inner); pw.print("mSwipeDistanceThreshold="); pw.println(mSwipeDistanceThreshold);
+ }
+
private final class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener {
private OverScroller mOverscroller;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 3f6708d..d89d212 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -480,7 +480,6 @@
private Dimmer mDimmer = new Dimmer(this);
private final Rect mTmpDimBoundsRect = new Rect();
- private final Point mLastSurfaceSize = new Point();
/** @see #setCanAffectSystemUiFlags */
private boolean mCanAffectSystemUiFlags = true;
@@ -4809,10 +4808,6 @@
notifyClients);
}, true /* traverseTopToBottom */);
- // Notify WM shell that task visibilities may have changed
- forAllTasks(task -> task.dispatchTaskInfoChangedIfNeeded(/* force */ false),
- true /* traverseTopToBottom */);
-
if (mTranslucentActivityWaiting != null &&
mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
// Nothing is getting drawn or everything was already visible, don't wait for
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index a257e90..66f2dbc 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1336,12 +1336,11 @@
for (int i = mLaunchRootTasks.size() - 1; i >= 0; --i) {
if (mLaunchRootTasks.get(i).contains(windowingMode, activityType)) {
final Task launchRootTask = mLaunchRootTasks.get(i).task;
- // Return the focusable root task for improving the UX with staged split screen.
final TaskFragment adjacentTaskFragment = launchRootTask != null
? launchRootTask.getAdjacentTaskFragment() : null;
final Task adjacentRootTask =
adjacentTaskFragment != null ? adjacentTaskFragment.asTask() : null;
- if (adjacentRootTask != null && adjacentRootTask.isFocusedRootTaskOnDisplay()) {
+ if (sourceTask != null && sourceTask.getRootTask() == adjacentRootTask) {
return adjacentRootTask;
} else {
return launchRootTask;
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index fce279d..2b5a820 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -240,6 +240,8 @@
*/
private int mTaskFragmentOrganizerPid = ActivityRecord.INVALID_PID;
+ final Point mLastSurfaceSize = new Point();
+
private final Rect mTmpInsets = new Rect();
private final Rect mTmpBounds = new Rect();
private final Rect mTmpFullBounds = new Rect();
@@ -1654,6 +1656,7 @@
}
}
+ @Override
void onChildPositionChanged(WindowContainer child) {
super.onChildPositionChanged(child);
@@ -2049,14 +2052,58 @@
if (shouldStartChangeTransition(mTmpPrevBounds)) {
initializeChangeTransition(mTmpPrevBounds);
} else if (mTaskFragmentOrganizer != null) {
- // Update the surface position here instead of in the organizer so that we can make sure
+ // Update the surface here instead of in the organizer so that we can make sure
// it can be synced with the surface freezer.
- updateSurfacePosition(getSyncTransaction());
+ final SurfaceControl.Transaction t = getSyncTransaction();
+ updateSurfacePosition(t);
+ updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */);
}
sendTaskFragmentInfoChanged();
}
+ /** Updates the surface size so that the sub windows cannot be shown out of bounds. */
+ private void updateOrganizedTaskFragmentSurfaceSize(SurfaceControl.Transaction t,
+ boolean forceUpdate) {
+ if (mTaskFragmentOrganizer == null) {
+ // We only want to update for organized TaskFragment. Task will handle itself.
+ return;
+ }
+ if (mSurfaceControl == null || mSurfaceAnimator.hasLeash() || mSurfaceFreezer.hasLeash()) {
+ return;
+ }
+
+ final Rect bounds = getBounds();
+ final int width = bounds.width();
+ final int height = bounds.height();
+ if (!forceUpdate && width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
+ return;
+ }
+ t.setWindowCrop(mSurfaceControl, width, height);
+ mLastSurfaceSize.set(width, height);
+ }
+
+ @Override
+ public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
+ super.onAnimationLeashCreated(t, leash);
+ // Reset surface bounds for animation. It will be taken care by the animation leash, and
+ // reset again onAnimationLeashLost.
+ if (mTaskFragmentOrganizer != null
+ && (mLastSurfaceSize.x != 0 || mLastSurfaceSize.y != 0)) {
+ t.setWindowCrop(mSurfaceControl, 0, 0);
+ mLastSurfaceSize.set(0, 0);
+ }
+ }
+
+ @Override
+ public void onAnimationLeashLost(SurfaceControl.Transaction t) {
+ super.onAnimationLeashLost(t);
+ // Update the surface bounds after animation.
+ if (mTaskFragmentOrganizer != null) {
+ updateOrganizedTaskFragmentSurfaceSize(t, true /* forceUpdate */);
+ }
+ }
+
/** Whether we should prepare a transition for this {@link TaskFragment} bounds change. */
private boolean shouldStartChangeTransition(Rect startBounds) {
if (mWmService.mDisableTransitionAnimation
@@ -2075,9 +2122,14 @@
@Override
void setSurfaceControl(SurfaceControl sc) {
super.setSurfaceControl(sc);
- // If the TaskFragmentOrganizer was set before we created the SurfaceControl, we need to
- // emit the callbacks now.
- sendTaskFragmentAppeared();
+ if (mTaskFragmentOrganizer != null) {
+ final SurfaceControl.Transaction t = getSyncTransaction();
+ updateSurfacePosition(t);
+ updateOrganizedTaskFragmentSurfaceSize(t, false /* forceUpdate */);
+ // If the TaskFragmentOrganizer was set before we created the SurfaceControl, we need to
+ // emit the callbacks now.
+ sendTaskFragmentAppeared();
+ }
}
void sendTaskFragmentInfoChanged() {
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 30d2a32..d911656 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -505,29 +505,46 @@
}
for (int i = 0, n = mPendingTaskFragmentEvents.size(); i < n; i++) {
PendingTaskFragmentEvent event = mPendingTaskFragmentEvents.get(i);
- final ITaskFragmentOrganizer taskFragmentOrg = event.mTaskFragmentOrg;
- final TaskFragment taskFragment = event.mTaskFragment;
- final TaskFragmentOrganizerState state =
- mTaskFragmentOrganizerState.get(taskFragmentOrg.asBinder());
- if (state == null) continue;
- switch (event.mEventType) {
- case PendingTaskFragmentEvent.EVENT_APPEARED:
- state.onTaskFragmentAppeared(taskFragmentOrg, taskFragment);
- break;
- case PendingTaskFragmentEvent.EVENT_VANISHED:
- state.onTaskFragmentVanished(taskFragmentOrg, taskFragment);
- break;
- case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
- state.onTaskFragmentInfoChanged(taskFragmentOrg, taskFragment);
- break;
- case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
- state.onTaskFragmentParentInfoChanged(taskFragmentOrg, taskFragment);
- break;
- case PendingTaskFragmentEvent.EVENT_ERROR:
- state.onTaskFragmentError(taskFragmentOrg, event.mErrorCallback,
- event.mException);
- }
+ dispatchEvent(event);
}
mPendingTaskFragmentEvents.clear();
}
+
+ void dispatchPendingInfoChangedEvent(TaskFragment taskFragment) {
+ PendingTaskFragmentEvent event = getPendingTaskFragmentEvent(taskFragment,
+ PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
+ if (event == null) {
+ return;
+ }
+
+ dispatchEvent(event);
+ mPendingTaskFragmentEvents.remove(event);
+ }
+
+ private void dispatchEvent(PendingTaskFragmentEvent event) {
+ final ITaskFragmentOrganizer taskFragmentOrg = event.mTaskFragmentOrg;
+ final TaskFragment taskFragment = event.mTaskFragment;
+ final TaskFragmentOrganizerState state =
+ mTaskFragmentOrganizerState.get(taskFragmentOrg.asBinder());
+ if (state == null) {
+ return;
+ }
+ switch (event.mEventType) {
+ case PendingTaskFragmentEvent.EVENT_APPEARED:
+ state.onTaskFragmentAppeared(taskFragmentOrg, taskFragment);
+ break;
+ case PendingTaskFragmentEvent.EVENT_VANISHED:
+ state.onTaskFragmentVanished(taskFragmentOrg, taskFragment);
+ break;
+ case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
+ state.onTaskFragmentInfoChanged(taskFragmentOrg, taskFragment);
+ break;
+ case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
+ state.onTaskFragmentParentInfoChanged(taskFragmentOrg, taskFragment);
+ break;
+ case PendingTaskFragmentEvent.EVENT_ERROR:
+ state.onTaskFragmentError(taskFragmentOrg, event.mErrorCallback,
+ event.mException);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index f43cd7a..9cc24e2 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -261,7 +261,8 @@
if (launchMode == WINDOWING_MODE_PINNED) {
if (DEBUG) appendLog("picture-in-picture");
} else if (!root.isResizeable()) {
- if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea)) {
+ if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea,
+ options.getLaunchWindowingMode())) {
launchMode = WINDOWING_MODE_FREEFORM;
if (outParams.mBounds.isEmpty()) {
getTaskBounds(root, suggestedDisplayArea, layout, launchMode,
@@ -617,7 +618,11 @@
}
private boolean shouldLaunchUnresizableAppInFreeform(ActivityRecord activity,
- TaskDisplayArea displayArea) {
+ TaskDisplayArea displayArea, int launchWindowingMode) {
+ if (launchWindowingMode == WINDOWING_MODE_FULLSCREEN) {
+ // Do not launch the activity in freeform if it explicitly requested fullscreen mode.
+ return false;
+ }
if (!activity.supportsFreeformInDisplayArea(displayArea) || activity.isResizeable()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index fe3e560..be00487 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2624,23 +2624,27 @@
* For now, this will only be called for the following cases:
* 1. {@link Task} is changing windowing mode between fullscreen and freeform.
* 2. {@link TaskFragment} is organized and is changing window bounds.
- * 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}.
+ * 3. {@link ActivityRecord} is reparented into an organized {@link TaskFragment}. (The
+ * transition will happen on the {@link TaskFragment} for this case).
*
- * This shouldn't be called on other {@link WindowContainer} unless there is a valid use case.
+ * This shouldn't be called on other {@link WindowContainer} unless there is a valid
+ * use case.
*
* @param startBounds The original bounds (on screen) of the surface we are snapshotting.
- * @param parentBounds The parent bounds (on screen) to calculate the animation surface
- * position.
+ * @param freezeTarget The surface to take snapshot from. If {@code null}, we will take a
+ * snapshot from {@link #getFreezeSnapshotTarget()}.
*/
- void initializeChangeTransition(Rect startBounds, Rect parentBounds) {
+ void initializeChangeTransition(Rect startBounds, @Nullable SurfaceControl freezeTarget) {
mDisplayContent.prepareAppTransition(TRANSIT_CHANGE);
mDisplayContent.mChangingContainers.add(this);
+ // Calculate the relative position in parent container.
+ final Rect parentBounds = getParent().getBounds();
mTmpPoint.set(startBounds.left - parentBounds.left, startBounds.top - parentBounds.top);
- mSurfaceFreezer.freeze(getSyncTransaction(), startBounds, mTmpPoint);
+ mSurfaceFreezer.freeze(getSyncTransaction(), startBounds, mTmpPoint, freezeTarget);
}
void initializeChangeTransition(Rect startBounds) {
- initializeChangeTransition(startBounds, getParent().getBounds());
+ initializeChangeTransition(startBounds, null /* freezeTarget */);
}
ArraySet<WindowContainer> getAnimationSources() {
@@ -3163,7 +3167,7 @@
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
void updateSurfacePosition(Transaction t) {
- if (mSurfaceControl == null || mSurfaceAnimator.hasLeash()) {
+ if (mSurfaceControl == null || mSurfaceAnimator.hasLeash() || mSurfaceFreezer.hasLeash()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e6615e1..50c3d3c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1772,17 +1772,15 @@
final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
- if (type == TYPE_APPLICATION_STARTING && activity != null) {
- activity.attachStartingWindow(win);
- ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
- activity, win);
- }
-
boolean imMayMove = true;
win.mToken.addWindow(win);
displayPolicy.addWindowLw(win, attrs);
- if (type == TYPE_INPUT_METHOD) {
+ if (type == TYPE_APPLICATION_STARTING && activity != null) {
+ activity.attachStartingWindow(win);
+ ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s",
+ activity, win);
+ } else if (type == TYPE_INPUT_METHOD) {
displayContent.setInputMethodWindowLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 47d7f03..0f8587c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -23,6 +23,9 @@
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_CENTER;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_LEFT;
+import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_RIGHT;
import android.content.res.Resources.NotFoundException;
import android.graphics.Color;
@@ -44,6 +47,7 @@
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
+import com.android.server.wm.LetterboxConfiguration.LetterboxReachabilityPosition;
import java.io.IOException;
import java.io.PrintWriter;
@@ -787,22 +791,33 @@
return 0;
}
- private int runSetLetterboxDefaultPositionMultiplierForReachability(PrintWriter pw)
+ private int runSetLetterboxDefaultPositionForReachability(PrintWriter pw)
throws RemoteException {
- final float multiplier;
+ @LetterboxReachabilityPosition final int position;
try {
String arg = getNextArgRequired();
- multiplier = Float.parseFloat(arg);
- } catch (NumberFormatException e) {
- getErrPrintWriter().println("Error: bad multiplier format " + e);
- return -1;
+ switch (arg) {
+ case "left":
+ position = LETTERBOX_REACHABILITY_POSITION_LEFT;
+ break;
+ case "center":
+ position = LETTERBOX_REACHABILITY_POSITION_CENTER;
+ break;
+ case "right":
+ position = LETTERBOX_REACHABILITY_POSITION_RIGHT;
+ break;
+ default:
+ getErrPrintWriter().println(
+ "Error: 'left', 'center' or 'right' are expected as an argument");
+ return -1;
+ }
} catch (IllegalArgumentException e) {
getErrPrintWriter().println(
- "Error: multiplier should be provided as an argument " + e);
+ "Error: 'left', 'center' or 'right' are expected as an argument" + e);
return -1;
}
synchronized (mInternal.mGlobalLock) {
- mLetterboxConfiguration.setDefaultPositionMultiplierForReachability(multiplier);
+ mLetterboxConfiguration.setDefaultPositionForReachability(position);
}
return 0;
}
@@ -841,8 +856,8 @@
case "--isReachabilityEnabled":
runSetLetterboxIsReachabilityEnabled(pw);
break;
- case "--defaultPositionMultiplierReachability":
- runSetLetterboxDefaultPositionMultiplierForReachability(pw);
+ case "--defaultPositionForReachability":
+ runSetLetterboxDefaultPositionForReachability(pw);
break;
default:
getErrPrintWriter().println(
@@ -885,8 +900,8 @@
case "isReachabilityEnabled":
mLetterboxConfiguration.getIsReachabilityEnabled();
break;
- case "defaultPositionMultiplierForReachability":
- mLetterboxConfiguration.getDefaultPositionMultiplierForReachability();
+ case "defaultPositionForReachability":
+ mLetterboxConfiguration.getDefaultPositionForReachability();
break;
default:
getErrPrintWriter().println(
@@ -982,7 +997,7 @@
mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha();
mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier();
mLetterboxConfiguration.resetIsReachabilityEnabled();
- mLetterboxConfiguration.resetDefaultPositionMultiplierForReachability();
+ mLetterboxConfiguration.resetDefaultPositionForReachability();
}
}
@@ -996,8 +1011,9 @@
+ mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
pw.println("Is reachability enabled: "
+ mLetterboxConfiguration.getIsReachabilityEnabled());
- pw.println("Default position multiplier for reachability: "
- + mLetterboxConfiguration.getDefaultPositionMultiplierForReachability());
+ pw.println("Default position for reachability: "
+ + LetterboxConfiguration.letterboxReachabilityPositionToString(
+ mLetterboxConfiguration.getDefaultPositionForReachability()));
pw.println("Background type: "
+ LetterboxConfiguration.letterboxBackgroundTypeToString(
@@ -1135,11 +1151,9 @@
pw.println(" --isReachabilityEnabled [true|1|false|0]");
pw.println(" Whether reachability repositioning is allowed for letterboxed");
pw.println(" fullscreen apps in landscape device orientation.");
- pw.println(" --defaultPositionMultiplierReachability multiplier");
- pw.println(" Default horizontal position of app window center when reachability is");
- pw.println(" enabled. If multiplier < 0.0 or > 1, both it and ");
- pw.println(" R.dimen.config_letterboxDefaultPositionMultiplierForReachability");
- pw.println(" are ignored and right position (1.0) is used.");
+ pw.println(" --defaultPositionForReachability [left|center|right]");
+ pw.println(" Default horizontal position of app window when reachability is.");
+ pw.println(" enabled.");
pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType");
pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha");
pw.println(" |horizontalPositionMultiplier|isReachabilityEnabled");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 6931381..781b53d 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -462,6 +462,10 @@
container.onRequestedOverrideConfigurationChanged(c);
}
effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
+ if (windowMask != 0 && container.isEmbedded()) {
+ // Changing bounds of the embedded TaskFragments may result in lifecycle changes.
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
}
if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
if (container.setFocusable(change.getFocusable())) {
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 5b73de7..af38641 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -264,7 +264,7 @@
}
onConfigurationChanged(atm.getGlobalConfiguration());
- mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mName);
+ mAtm.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, mInfo.packageName);
}
public void setPid(int pid) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ed98b9d..9268d82 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -308,6 +308,8 @@
@NonNull WindowToken mToken;
// The same object as mToken if this is an app window and null for non-app windows.
ActivityRecord mActivityRecord;
+ /** Non-null if this is a starting window. */
+ StartingData mStartingData;
// mAttrs.flags is tested in animation without being locked. If the bits tested are ever
// modified they will need to be locked.
@@ -5485,6 +5487,10 @@
return mWillReplaceWindow;
}
+ private boolean isStartingWindowAssociatedToTask() {
+ return mStartingData != null && mStartingData.mAssociatedTask != null;
+ }
+
private void applyDims() {
if (!mAnimatingExit && mAppDied) {
mIsDimming = true;
@@ -5634,7 +5640,9 @@
outPoint.offset(-parent.mWindowFrames.mFrame.left + mTmpPoint.x,
-parent.mWindowFrames.mFrame.top + mTmpPoint.y);
} else if (parentWindowContainer != null) {
- final Rect parentBounds = parentWindowContainer.getBounds();
+ final Rect parentBounds = isStartingWindowAssociatedToTask()
+ ? mStartingData.mAssociatedTask.getBounds()
+ : parentWindowContainer.getBounds();
outPoint.offset(-parentBounds.left, -parentBounds.top);
}
@@ -5717,6 +5725,11 @@
@Override
void assignLayer(Transaction t, int layer) {
+ if (isStartingWindowAssociatedToTask()) {
+ // The starting window should cover the task.
+ t.setLayer(mSurfaceControl, Integer.MAX_VALUE);
+ return;
+ }
// See comment in assignRelativeLayerForImeTargetChild
if (needsRelativeLayeringToIme()) {
getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
@@ -5729,6 +5742,24 @@
return mIsDimming;
}
+ @Override
+ protected void reparentSurfaceControl(Transaction t, SurfaceControl newParent) {
+ if (isStartingWindowAssociatedToTask()) {
+ // Its surface is already put in task. Don't reparent when transferring starting window
+ // across activities.
+ return;
+ }
+ super.reparentSurfaceControl(t, newParent);
+ }
+
+ @Override
+ public SurfaceControl getAnimationLeashParent() {
+ if (isStartingWindowAssociatedToTask()) {
+ return mStartingData.mAssociatedTask.mSurfaceControl;
+ }
+ return super.getAnimationLeashParent();
+ }
+
// TODO(b/70040778): We should aim to eliminate the last user of TYPE_APPLICATION_MEDIA
// then we can drop all negative layering on the windowing side and simply inherit
// the default implementation here.
diff --git a/services/core/jni/com_android_server_UsbMidiDevice.cpp b/services/core/jni/com_android_server_UsbMidiDevice.cpp
index d6b5bed..c8e7698 100644
--- a/services/core/jni/com_android_server_UsbMidiDevice.cpp
+++ b/services/core/jni/com_android_server_UsbMidiDevice.cpp
@@ -24,14 +24,14 @@
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
-#include <stdio.h>
-#include <errno.h>
#include <asm/byteorder.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <errno.h>
#include <fcntl.h>
-#include <sys/ioctl.h>
#include <sound/asound.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
namespace android
{
@@ -39,78 +39,62 @@
static jclass sFileDescriptorClass;
static jfieldID sPipeFDField;
-static jint
-android_server_UsbMidiDevice_get_subdevice_count(JNIEnv *env, jobject /* thiz */,
- jint card, jint device)
-{
+// This function returns an array of integers, each representing a file descriptor.
+// The will be in the order of inputs then outputs.
+// The last input fd will be for a file descriptor that simply allows Os.poll() to keep working.
+// For example, if numInputs is 2 and numOutputs is 1, the resulting fds are as follows:
+// 1. Input O_RDONLY file descriptor
+// 2. Special input file descriptor to block the input thread
+// 3. Output O_WRONLY file descriptor
+static jobjectArray android_server_UsbMidiDevice_open(JNIEnv *env, jobject thiz, jint card,
+ jint device, jint numInputs,
+ jint numOutputs) {
char path[100];
- int fd;
- const int kMaxRetries = 10;
- const int kSleepMicroseconds = 2000;
-
- snprintf(path, sizeof(path), "/dev/snd/controlC%d", card);
- // This control device may not have been created yet. So we should
- // try to open it several times to prevent intermittent failure
- // from a race condition.
- int retryCounter = 0;
- while ((fd = open(path, O_RDWR)) < 0) {
- if (++retryCounter > kMaxRetries) {
- ALOGE("timed out after %d tries, could not open %s", retryCounter, path);
- return 0;
- } else {
- ALOGW("attempt #%d, could not open %s", retryCounter, path);
- // Increase the sleep interval each time.
- // 10 retries will total 2 * sum(1..10) = 110 milliseconds.
- // Typically the device should be ready in 5-10 milliseconds.
- usleep(kSleepMicroseconds * retryCounter);
- }
- }
-
- struct snd_rawmidi_info info;
- memset(&info, 0, sizeof(info));
- info.device = device;
- int ret = ioctl(fd, SNDRV_CTL_IOCTL_RAWMIDI_INFO, &info);
- close(fd);
-
- if (ret < 0) {
- ALOGE("SNDRV_CTL_IOCTL_RAWMIDI_INFO failed, errno: %d path: %s", errno, path);
- return -1;
- }
-
- ALOGD("subdevices_count: %d", info.subdevices_count);
- return info.subdevices_count;
-}
-
-static jobjectArray
-android_server_UsbMidiDevice_open(JNIEnv *env, jobject thiz, jint card, jint device,
- jint subdevice_count)
-{
- char path[100];
+ int fd;
snprintf(path, sizeof(path), "/dev/snd/midiC%dD%d", card, device);
- // allocate one extra file descriptor for close pipe
- jobjectArray fds = env->NewObjectArray(subdevice_count + 1, sFileDescriptorClass, NULL);
+ ALOGD("Opening %d inputs and %d outputs", numInputs, numOutputs);
+
+ jobjectArray fds = env->NewObjectArray(numInputs + numOutputs, sFileDescriptorClass, NULL);
if (!fds) {
return NULL;
}
- // to support multiple subdevices we open the same file multiple times
- for (int i = 0; i < subdevice_count; i++) {
- int fd = open(path, O_RDWR);
+ // open the path for the read pipes. The last one is special and used to
+ // unblock Os.poll()
+ for (int i = 0; i < numInputs - 1; i++) {
+ fd = open(path, O_RDONLY);
if (fd < 0) {
ALOGE("open failed on %s for index %d", path, i);
goto release_fds;
}
ScopedLocalRef<jobject> jifd(env, jniCreateFileDescriptor(env, fd));
if (jifd.get() == NULL) {
+ close(fd);
goto release_fds;
}
env->SetObjectArrayElement(fds, i, jifd.get());
}
- // create a pipe to use for unblocking our input thread
- {
+ // open the path for the write pipes
+ for (int i = 0; i < numOutputs; i++) {
+ fd = open(path, O_WRONLY);
+ if (fd < 0) {
+ ALOGE("open failed on %s for index %d", path, i);
+ goto release_fds;
+ }
+ ScopedLocalRef<jobject> jifd(env, jniCreateFileDescriptor(env, fd));
+ if (jifd.get() == NULL) {
+ close(fd);
+ goto release_fds;
+ }
+ env->SetObjectArrayElement(fds, i + numInputs, jifd.get());
+ }
+
+ // create a pipe to use for unblocking our input thread. The caller should
+ // set numInputs as 0 when there are zero real input threads.
+ if (numInputs > 0) {
int pipeFD[2];
if (pipe(pipeFD) == -1) {
ALOGE("pipe() failed, errno = %d", errno);
@@ -123,20 +107,21 @@
close(pipeFD[1]);
goto release_fds;
}
- env->SetObjectArrayElement(fds, subdevice_count, jifd.get());
+
+ // store as last input file descriptor
+ env->SetObjectArrayElement(fds, numInputs - 1, jifd.get());
// store our end of the pipe in mPipeFD
env->SetIntField(thiz, sPipeFDField, pipeFD[1]);
}
return fds;
release_fds:
- for (int i = 0; i < subdevice_count + 1; ++i) {
+ for (int i = 0; i < numInputs + numOutputs; ++i) {
ScopedLocalRef<jobject> jifd(env, env->GetObjectArrayElement(fds, i));
- if (jifd.get() == NULL) {
- break;
+ if (jifd.get() != NULL) {
+ int fd = jniGetFDFromFileDescriptor(env, jifd.get());
+ close(fd);
}
- int fd = jniGetFDFromFileDescriptor(env, jifd.get());
- close(fd);
}
return NULL;
}
@@ -158,9 +143,9 @@
}
static JNINativeMethod method_table[] = {
- { "nativeGetSubdeviceCount", "(II)I", (void*)android_server_UsbMidiDevice_get_subdevice_count },
- { "nativeOpen", "(III)[Ljava/io/FileDescriptor;", (void*)android_server_UsbMidiDevice_open },
- { "nativeClose", "([Ljava/io/FileDescriptor;)V", (void*)android_server_UsbMidiDevice_close },
+ {"nativeOpen", "(IIII)[Ljava/io/FileDescriptor;",
+ (void *)android_server_UsbMidiDevice_open},
+ {"nativeClose", "([Ljava/io/FileDescriptor;)V", (void *)android_server_UsbMidiDevice_close},
};
int register_android_server_UsbMidiDevice(JNIEnv *env)
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index bb9740b..94b1ad1 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -451,13 +451,13 @@
}
dump += "\n";
- mInputManager->getReader()->dump(dump);
+ mInputManager->getReader().dump(dump);
dump += "\n";
- mInputManager->getClassifier()->dump(dump);
+ mInputManager->getClassifier().dump(dump);
dump += "\n";
- mInputManager->getDispatcher()->dump(dump);
+ mInputManager->getDispatcher().dump(dump);
dump += "\n";
}
@@ -505,33 +505,33 @@
}
} // release lock
- mInputManager->getReader()->requestRefreshConfiguration(
+ mInputManager->getReader().requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputChannel(
JNIEnv* /* env */, const std::string& name) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->createInputChannel(name);
+ return mInputManager->getDispatcher().createInputChannel(name);
}
base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputMonitor(
JNIEnv* /* env */, int32_t displayId, bool isGestureMonitor, const std::string& name,
int32_t pid) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->createInputMonitor(displayId, isGestureMonitor, name,
- pid);
+ return mInputManager->getDispatcher().createInputMonitor(displayId, isGestureMonitor, name,
+ pid);
}
status_t NativeInputManager::removeInputChannel(JNIEnv* /* env */,
const sp<IBinder>& connectionToken) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->removeInputChannel(connectionToken);
+ return mInputManager->getDispatcher().removeInputChannel(connectionToken);
}
status_t NativeInputManager::pilferPointers(const sp<IBinder>& token) {
ATRACE_CALL();
- return mInputManager->getDispatcher()->pilferPointers(token);
+ return mInputManager->getDispatcher().pilferPointers(token);
}
void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) {
@@ -998,7 +998,7 @@
}
void NativeInputManager::displayRemoved(JNIEnv* env, int32_t displayId) {
- mInputManager->getDispatcher()->displayRemoved(displayId);
+ mInputManager->getDispatcher().displayRemoved(displayId);
}
void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId,
@@ -1009,15 +1009,15 @@
std::shared_ptr<InputApplicationHandle> applicationHandle =
android_view_InputApplicationHandle_getHandle(env, applicationHandleObj);
applicationHandle->updateInfo();
- mInputManager->getDispatcher()->setFocusedApplication(displayId, applicationHandle);
+ mInputManager->getDispatcher().setFocusedApplication(displayId, applicationHandle);
}
void NativeInputManager::setFocusedDisplay(JNIEnv* env, int32_t displayId) {
- mInputManager->getDispatcher()->setFocusedDisplay(displayId);
+ mInputManager->getDispatcher().setFocusedDisplay(displayId);
}
void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) {
- mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen);
+ mInputManager->getDispatcher().setInputDispatchMode(enabled, frozen);
}
void NativeInputManager::setSystemUiLightsOut(bool lightsOut) {
@@ -1051,7 +1051,7 @@
mLocked.pointerSpeed = speed;
} // release lock
- mInputManager->getReader()->requestRefreshConfiguration(
+ mInputManager->getReader().requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_POINTER_SPEED);
}
@@ -1069,7 +1069,7 @@
}
} // release lock
- mInputManager->getReader()->requestRefreshConfiguration(
+ mInputManager->getReader().requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_ENABLED_STATE);
}
@@ -1085,12 +1085,12 @@
mLocked.showTouches = enabled;
} // release lock
- mInputManager->getReader()->requestRefreshConfiguration(
+ mInputManager->getReader().requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
}
void NativeInputManager::requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) {
- mInputManager->getDispatcher()->requestPointerCapture(windowToken, enabled);
+ mInputManager->getDispatcher().requestPointerCapture(windowToken, enabled);
}
void NativeInputManager::setInteractive(bool interactive) {
@@ -1098,7 +1098,7 @@
}
void NativeInputManager::reloadCalibration() {
- mInputManager->getReader()->requestRefreshConfiguration(
+ mInputManager->getReader().requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_TOUCH_AFFINE_TRANSFORMATION);
}
@@ -1396,7 +1396,7 @@
mLocked.pointerCaptureRequest = request;
} // release lock
- mInputManager->getReader()->requestRefreshConfiguration(
+ mInputManager->getReader().requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_POINTER_CAPTURE);
}
@@ -1481,7 +1481,7 @@
}
void NativeInputManager::setMotionClassifierEnabled(bool enabled) {
- mInputManager->getClassifier()->setMotionClassifierEnabled(enabled);
+ mInputManager->getClassifier().setMotionClassifierEnabled(enabled);
}
// ----------------------------------------------------------------------------
@@ -1519,24 +1519,24 @@
jlong ptr, jint deviceId, jint sourceMask, jint scanCode) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- return (jint) im->getInputManager()->getReader()->getScanCodeState(
- deviceId, uint32_t(sourceMask), scanCode);
+ return (jint)im->getInputManager()->getReader().getScanCodeState(deviceId, uint32_t(sourceMask),
+ scanCode);
}
static jint nativeGetKeyCodeState(JNIEnv* /* env */, jclass /* clazz */,
jlong ptr, jint deviceId, jint sourceMask, jint keyCode) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- return (jint) im->getInputManager()->getReader()->getKeyCodeState(
- deviceId, uint32_t(sourceMask), keyCode);
+ return (jint)im->getInputManager()->getReader().getKeyCodeState(deviceId, uint32_t(sourceMask),
+ keyCode);
}
static jint nativeGetSwitchState(JNIEnv* /* env */, jclass /* clazz */,
jlong ptr, jint deviceId, jint sourceMask, jint sw) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- return (jint) im->getInputManager()->getReader()->getSwitchState(
- deviceId, uint32_t(sourceMask), sw);
+ return (jint)im->getInputManager()->getReader().getSwitchState(deviceId, uint32_t(sourceMask),
+ sw);
}
static jboolean nativeHasKeys(JNIEnv* env, jclass /* clazz */,
@@ -1548,8 +1548,8 @@
jsize numCodes = env->GetArrayLength(keyCodes);
jboolean result;
if (numCodes == env->GetArrayLength(keyCodes)) {
- if (im->getInputManager()->getReader()->hasKeys(
- deviceId, uint32_t(sourceMask), numCodes, codes, flags)) {
+ if (im->getInputManager()->getReader().hasKeys(deviceId, uint32_t(sourceMask), numCodes,
+ codes, flags)) {
result = JNI_TRUE;
} else {
result = JNI_FALSE;
@@ -1655,28 +1655,28 @@
jlong ptr, jboolean enabled) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getDispatcher()->setInputFilterEnabled(enabled);
+ im->getInputManager()->getDispatcher().setInputFilterEnabled(enabled);
}
static void nativeSetInTouchMode(JNIEnv* /* env */, jclass /* clazz */,
jlong ptr, jboolean inTouchMode) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getDispatcher()->setInTouchMode(inTouchMode);
+ im->getInputManager()->getDispatcher().setInTouchMode(inTouchMode);
}
static void nativeSetMaximumObscuringOpacityForTouch(JNIEnv* /* env */, jclass /* clazz */,
jlong ptr, jfloat opacity) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getDispatcher()->setMaximumObscuringOpacityForTouch(opacity);
+ im->getInputManager()->getDispatcher().setMaximumObscuringOpacityForTouch(opacity);
}
static void nativeSetBlockUntrustedTouchesMode(JNIEnv* env, jclass /* clazz */, jlong ptr,
jint mode) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getDispatcher()->setBlockUntrustedTouchesMode(
+ im->getInputManager()->getDispatcher().setBlockUntrustedTouchesMode(
static_cast<BlockUntrustedTouchesMode>(mode));
}
@@ -1697,11 +1697,11 @@
}
const InputEventInjectionResult result =
- im->getInputManager()->getDispatcher()->injectInputEvent(&keyEvent, injectorPid,
- injectorUid, mode,
- std::chrono::milliseconds(
- timeoutMillis),
- uint32_t(policyFlags));
+ im->getInputManager()->getDispatcher().injectInputEvent(&keyEvent, injectorPid,
+ injectorUid, mode,
+ std::chrono::milliseconds(
+ timeoutMillis),
+ uint32_t(policyFlags));
return static_cast<jint>(result);
} else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) {
const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj);
@@ -1711,11 +1711,11 @@
}
const InputEventInjectionResult result =
- im->getInputManager()->getDispatcher()->injectInputEvent(motionEvent, injectorPid,
- injectorUid, mode,
- std::chrono::milliseconds(
- timeoutMillis),
- uint32_t(policyFlags));
+ im->getInputManager()->getDispatcher().injectInputEvent(motionEvent, injectorPid,
+ injectorUid, mode,
+ std::chrono::milliseconds(
+ timeoutMillis),
+ uint32_t(policyFlags));
return static_cast<jint>(result);
} else {
jniThrowRuntimeException(env, "Invalid input event type.");
@@ -1736,7 +1736,7 @@
}
std::unique_ptr<VerifiedInputEvent> verifiedEvent =
- im->getInputManager()->getDispatcher()->verifyInputEvent(keyEvent);
+ im->getInputManager()->getDispatcher().verifyInputEvent(keyEvent);
if (verifiedEvent == nullptr) {
return nullptr;
}
@@ -1751,7 +1751,7 @@
}
std::unique_ptr<VerifiedInputEvent> verifiedEvent =
- im->getInputManager()->getDispatcher()->verifyInputEvent(*motionEvent);
+ im->getInputManager()->getDispatcher().verifyInputEvent(*motionEvent);
if (verifiedEvent == nullptr) {
return nullptr;
@@ -1770,7 +1770,7 @@
jlong ptr, jint deviceId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getReader()->toggleCapsLockState(deviceId);
+ im->getInputManager()->getReader().toggleCapsLockState(deviceId);
}
static void nativeDisplayRemoved(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId) {
@@ -1826,8 +1826,8 @@
sp<IBinder> toChannelToken = ibinderForJavaObject(env, toChannelTokenObj);
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- if (im->getInputManager()->getDispatcher()->transferTouchFocus(fromChannelToken, toChannelToken,
- isDragDrop)) {
+ if (im->getInputManager()->getDispatcher().transferTouchFocus(fromChannelToken, toChannelToken,
+ isDragDrop)) {
return JNI_TRUE;
} else {
return JNI_FALSE;
@@ -1839,7 +1839,7 @@
sp<IBinder> destChannelToken = ibinderForJavaObject(env, destChannelTokenObj);
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- if (im->getInputManager()->getDispatcher()->transferTouch(destChannelToken)) {
+ if (im->getInputManager()->getDispatcher().transferTouch(destChannelToken)) {
return JNI_TRUE;
} else {
return JNI_FALSE;
@@ -1889,7 +1889,7 @@
jint* amplitudes = static_cast<jint*>(env->GetPrimitiveArrayCritical(amplitudesObj, nullptr));
VibrationSequence sequence(patternSize);
- std::vector<int32_t> vibrators = im->getInputManager()->getReader()->getVibratorIds(deviceId);
+ std::vector<int32_t> vibrators = im->getInputManager()->getReader().getVibratorIds(deviceId);
for (size_t i = 0; i < patternSize; i++) {
// VibrationEffect.validate guarantees duration > 0.
std::chrono::milliseconds duration(patternMillis[i]);
@@ -1904,7 +1904,7 @@
env->ReleasePrimitiveArrayCritical(patternObj, patternMillis, JNI_ABORT);
env->ReleasePrimitiveArrayCritical(amplitudesObj, amplitudes, JNI_ABORT);
- im->getInputManager()->getReader()->vibrate(deviceId, sequence, repeat, token);
+ im->getInputManager()->getReader().vibrate(deviceId, sequence, repeat, token);
}
static void nativeVibrateCombined(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
@@ -1954,25 +1954,25 @@
sequence.addElement(element);
}
- im->getInputManager()->getReader()->vibrate(deviceId, sequence, repeat, token);
+ im->getInputManager()->getReader().vibrate(deviceId, sequence, repeat, token);
}
static void nativeCancelVibrate(JNIEnv* /* env */,
jclass /* clazz */, jlong ptr, jint deviceId, jint token) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getReader()->cancelVibrate(deviceId, token);
+ im->getInputManager()->getReader().cancelVibrate(deviceId, token);
}
static bool nativeIsVibrating(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint deviceId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- return im->getInputManager()->getReader()->isVibrating(deviceId);
+ return im->getInputManager()->getReader().isVibrating(deviceId);
}
static jintArray nativeGetVibratorIds(JNIEnv* env, jclass clazz, jlong ptr, jint deviceId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- std::vector<int32_t> vibrators = im->getInputManager()->getReader()->getVibratorIds(deviceId);
+ std::vector<int32_t> vibrators = im->getInputManager()->getReader().getVibratorIds(deviceId);
jintArray vibIdArray = env->NewIntArray(vibrators.size());
if (vibIdArray != nullptr) {
@@ -1986,7 +1986,7 @@
jobject jLights = env->NewObject(gArrayListClassInfo.clazz, gArrayListClassInfo.constructor);
std::vector<InputDeviceLightInfo> lights =
- im->getInputManager()->getReader()->getLights(deviceId);
+ im->getInputManager()->getReader().getLights(deviceId);
for (size_t i = 0; i < lights.size(); i++) {
const InputDeviceLightInfo& lightInfo = lights[i];
@@ -2031,7 +2031,7 @@
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
std::optional<int32_t> ret =
- im->getInputManager()->getReader()->getLightPlayerId(deviceId, lightId);
+ im->getInputManager()->getReader().getLightPlayerId(deviceId, lightId);
return static_cast<jint>(ret.value_or(0));
}
@@ -2041,7 +2041,7 @@
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
std::optional<int32_t> ret =
- im->getInputManager()->getReader()->getLightColor(deviceId, lightId);
+ im->getInputManager()->getReader().getLightColor(deviceId, lightId);
return static_cast<jint>(ret.value_or(0));
}
@@ -2049,27 +2049,27 @@
jint lightId, jint playerId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getReader()->setLightPlayerId(deviceId, lightId, playerId);
+ im->getInputManager()->getReader().setLightPlayerId(deviceId, lightId, playerId);
}
static void nativeSetLightColor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
jint lightId, jint color) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getReader()->setLightColor(deviceId, lightId, color);
+ im->getInputManager()->getReader().setLightColor(deviceId, lightId, color);
}
static jint nativeGetBatteryCapacity(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- std::optional<int32_t> ret = im->getInputManager()->getReader()->getBatteryCapacity(deviceId);
+ std::optional<int32_t> ret = im->getInputManager()->getReader().getBatteryCapacity(deviceId);
return static_cast<jint>(ret.value_or(android::os::IInputConstants::INVALID_BATTERY_CAPACITY));
}
static jint nativeGetBatteryStatus(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- std::optional<int32_t> ret = im->getInputManager()->getReader()->getBatteryStatus(deviceId);
+ std::optional<int32_t> ret = im->getInputManager()->getReader().getBatteryStatus(deviceId);
return static_cast<jint>(ret.value_or(BATTERY_STATUS_UNKNOWN));
}
@@ -2077,7 +2077,7 @@
jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getReader()->requestRefreshConfiguration(
+ im->getInputManager()->getReader().requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS);
}
@@ -2085,7 +2085,7 @@
jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getReader()->requestRefreshConfiguration(
+ im->getInputManager()->getReader().requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_DEVICE_ALIAS);
}
@@ -2100,15 +2100,15 @@
static void nativeMonitor(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getReader()->monitor();
- im->getInputManager()->getDispatcher()->monitor();
+ im->getInputManager()->getReader().monitor();
+ im->getInputManager()->getDispatcher().monitor();
}
static jboolean nativeIsInputDeviceEnabled(JNIEnv* env /* env */,
jclass /* clazz */, jlong ptr, jint deviceId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- return im->getInputManager()->getReader()->isInputDeviceEnabled(deviceId);
+ return im->getInputManager()->getReader().isInputDeviceEnabled(deviceId);
}
static void nativeEnableInputDevice(JNIEnv* /* env */,
@@ -2157,18 +2157,18 @@
jint deviceId, jint displayId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- return im->getInputManager()->getReader()->canDispatchToDisplay(deviceId, displayId);
+ return im->getInputManager()->getReader().canDispatchToDisplay(deviceId, displayId);
}
static void nativeNotifyPortAssociationsChanged(JNIEnv* env, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getReader()->requestRefreshConfiguration(
+ im->getInputManager()->getReader().requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
static void nativeChangeUniqueIdAssociation(JNIEnv* env, jclass /* clazz */, jlong ptr) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getReader()->requestRefreshConfiguration(
+ im->getInputManager()->getReader().requestRefreshConfiguration(
InputReaderConfiguration::CHANGE_DISPLAY_INFO);
}
@@ -2213,7 +2213,7 @@
static jobjectArray nativeGetSensorList(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
std::vector<InputDeviceSensorInfo> sensors =
- im->getInputManager()->getReader()->getSensors(deviceId);
+ im->getInputManager()->getReader().getSensors(deviceId);
jobjectArray arr = env->NewObjectArray(sensors.size(), gInputSensorInfo.clazz, nullptr);
for (int i = 0; i < sensors.size(); i++) {
@@ -2247,29 +2247,29 @@
return im->getInputManager()
->getReader()
- ->enableSensor(deviceId, static_cast<InputDeviceSensorType>(sensorType),
- std::chrono::microseconds(samplingPeriodUs),
- std::chrono::microseconds(maxBatchReportLatencyUs));
+ .enableSensor(deviceId, static_cast<InputDeviceSensorType>(sensorType),
+ std::chrono::microseconds(samplingPeriodUs),
+ std::chrono::microseconds(maxBatchReportLatencyUs));
}
static void nativeDisableSensor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
jint sensorType) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getReader()->disableSensor(deviceId,
- static_cast<InputDeviceSensorType>(
- sensorType));
+ im->getInputManager()->getReader().disableSensor(deviceId,
+ static_cast<InputDeviceSensorType>(
+ sensorType));
}
static jboolean nativeFlushSensor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
jint sensorType) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getReader()->flushSensor(deviceId,
- static_cast<InputDeviceSensorType>(sensorType));
- return im->getInputManager()->getDispatcher()->flushSensor(deviceId,
- static_cast<InputDeviceSensorType>(
- sensorType));
+ im->getInputManager()->getReader().flushSensor(deviceId,
+ static_cast<InputDeviceSensorType>(sensorType));
+ return im->getInputManager()->getDispatcher().flushSensor(deviceId,
+ static_cast<InputDeviceSensorType>(
+ sensorType));
}
// ----------------------------------------------------------------------------
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 7fea547..fe86ff1 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -178,9 +178,10 @@
void enableAutoSuspend() {
static bool enabled = false;
if (!enabled) {
+ static sp<IBinder> autosuspendClientToken = new BBinder();
sp<system::suspend::internal::ISuspendControlServiceInternal> suspendControl =
getSuspendControlInternal();
- suspendControl->enableAutosuspend(&enabled);
+ suspendControl->enableAutosuspend(autosuspendClientToken, &enabled);
}
{
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a0afb17..00e58ef 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8353,7 +8353,8 @@
}
@Override
- public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId) {
+ public boolean setDeviceOwner(ComponentName admin, String ownerName, int userId,
+ boolean setProfileOwnerOnCurrentUserIfNecessary) {
if (!mHasFeature) {
logMissingFeatureAction("Cannot set " + ComponentName.flattenToShortString(admin)
+ " as device owner for user " + userId);
@@ -8416,7 +8417,8 @@
Slogf.i(LOG_TAG, "Device owner set: " + admin + " on user " + userId);
- if (mInjector.userManagerIsHeadlessSystemUserMode()) {
+ if (setProfileOwnerOnCurrentUserIfNecessary
+ && mInjector.userManagerIsHeadlessSystemUserMode()) {
int currentForegroundUser = getCurrentForegroundUserId();
Slogf.i(LOG_TAG, "setDeviceOwner(): setting " + admin
+ " as profile owner on user " + currentForegroundUser);
@@ -14122,6 +14124,7 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
+ Preconditions.checkCallAuthorization(canManageUsers(caller));
Preconditions.checkCallAuthorization(isManagedProfile(userHandle),
"You can not get organization name outside a managed profile, userId = %d",
userHandle);
@@ -17474,7 +17477,8 @@
// TODO(b/178187130): Directly set DO and remove the check once silent provisioning is no
// longer used.
if (getDeviceOwnerComponent(/* callingUserOnly= */ true) == null) {
- return setDeviceOwner(adminComponent, name, userId);
+ return setDeviceOwner(adminComponent, name, userId,
+ /* setProfileOwnerOnCurrentUserIfNecessary= */ true);
}
return true;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
index 85fe65c..e1d720c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java
@@ -46,11 +46,13 @@
private static final String USER_OPTION = "--user";
private static final String NAME_OPTION = "--name";
+ private static final String DO_ONLY_OPTION = "--device-owner-only";
private final DevicePolicyManagerService mService;
private int mUserId = UserHandle.USER_SYSTEM;
private String mName = "";
private ComponentName mComponent;
+ private boolean mSetDoOnly;
DevicePolicyManagerServiceShellCommand(DevicePolicyManagerService service) {
mService = Objects.requireNonNull(service);
@@ -130,8 +132,8 @@
pw.printf(" %s [ %s <USER_ID> | current ] <COMPONENT>\n",
CMD_SET_ACTIVE_ADMIN, USER_OPTION);
pw.printf(" Sets the given component as active admin for an existing user.\n\n");
- pw.printf(" %s [ %s <USER_ID> | current *EXPERIMENTAL* ] [ %s <NAME> ] "
- + "<COMPONENT>\n", CMD_SET_DEVICE_OWNER, USER_OPTION, NAME_OPTION);
+ pw.printf(" %s [ %s <USER_ID> | current *EXPERIMENTAL* ] [ %s <NAME> ] [ %s ]"
+ + "<COMPONENT>\n", CMD_SET_DEVICE_OWNER, USER_OPTION, NAME_OPTION, DO_ONLY_OPTION);
pw.printf(" Sets the given component as active admin, and its package as device owner."
+ "\n\n");
pw.printf(" %s [ %s <USER_ID> | current ] [ %s <NAME> ] <COMPONENT>\n",
@@ -254,7 +256,8 @@
mService.setActiveAdmin(mComponent, /* refreshing= */ true, mUserId);
try {
- if (!mService.setDeviceOwner(mComponent, mName, mUserId)) {
+ if (!mService.setDeviceOwner(mComponent, mName, mUserId,
+ /* setProfileOwnerOnCurrentUserIfNecessary= */ !mSetDoOnly)) {
throw new RuntimeException(
"Can't set package " + mComponent + " as device owner.");
}
@@ -351,6 +354,8 @@
if (mUserId == UserHandle.USER_CURRENT) {
mUserId = ActivityManager.getCurrentUser();
}
+ } else if (DO_ONLY_OPTION.equals(opt)) {
+ mSetDoOnly = true;
} else if (canHaveName && NAME_OPTION.equals(opt)) {
mName = getNextArgRequired();
} else {
diff --git a/services/net/java/android/net/ConnectivityModuleConnector.java b/services/net/java/android/net/ConnectivityModuleConnector.java
index 62f2c35..c6b15c1 100644
--- a/services/net/java/android/net/ConnectivityModuleConnector.java
+++ b/services/net/java/android/net/ConnectivityModuleConnector.java
@@ -278,7 +278,10 @@
// This code path is only run by the system server: only the system server binds
// to the NetworkStack as a service. Other processes get the NetworkStack from
// the ServiceManager.
- maybeCrashWithTerribleFailure("Lost network stack", mPackageName);
+ maybeCrashWithTerribleFailure(
+ "Lost network stack. This is not the root cause of any issue, it is a side "
+ + "effect of a crash that happened earlier. Earlier logs should point to the "
+ + "actual issue.", mPackageName);
}
}
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index b4f8b6e..9d86081 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -24,14 +24,7 @@
import android.os.Binder
import android.os.UserHandle
import android.util.ArrayMap
-import com.android.server.pm.AppsFilter
-import com.android.server.pm.ComponentResolver
-import com.android.server.pm.PackageManagerService
-import com.android.server.pm.PackageManagerTracedLock
-import com.android.server.pm.PackageSetting
-import com.android.server.pm.Settings
-import com.android.server.pm.UserManagerInternal
-import com.android.server.pm.UserManagerService
+import com.android.server.pm.*
import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.parsing.pkg.ParsedPackage
@@ -175,7 +168,7 @@
lateinit var params: Params
private lateinit var testHandler: TestHandler
- private lateinit var mockPendingBroadcasts: PackageManagerService.PendingPackageBroadcasts
+ private lateinit var mockPendingBroadcasts: PendingPackageBroadcasts
private lateinit var mockPkg: AndroidPackage
private lateinit var mockPkgSetting: PackageSetting
private lateinit var service: PackageManagerService
@@ -194,7 +187,7 @@
testHandler.sendEmptyMessage(SEND_PENDING_BROADCAST)
}
- mockPendingBroadcasts = PackageManagerService.PendingPackageBroadcasts()
+ mockPendingBroadcasts = PendingPackageBroadcasts()
service = mockService()
}
@@ -361,7 +354,7 @@
PackageManager.PERMISSION_GRANTED
}
}
- val mockInjector: PackageManagerService.Injector = mock {
+ val mockInjector: PackageManagerServiceInjector = mock {
whenever(this.lock) { PackageManagerTracedLock() }
whenever(this.componentResolver) { mockComponentResolver }
whenever(this.userManagerService) { mockUserManagerService }
@@ -374,7 +367,7 @@
whenever(this.context) { mockContext }
whenever(this.getHandler()) { testHandler }
}
- val testParams = PackageManagerService.TestParams().apply {
+ val testParams = PackageManagerServiceTestParams().apply {
this.pendingPackageBroadcasts = mockPendingBroadcasts
this.resolveComponentName = ComponentName("android", ".Test")
this.packages = ArrayMap<String, AndroidPackage>().apply { putAll(mockedPkgs) }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
index 03f1124..74ae7a9 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationPackageTest.kt
@@ -20,6 +20,7 @@
import android.content.pm.PackageManager
import android.content.pm.PackageUserState
import android.content.pm.Signature
+import android.content.pm.SigningDetails
import android.content.pm.parsing.component.ParsedActivity
import android.content.pm.parsing.component.ParsedIntentInfo
import android.content.pm.verify.domain.DomainOwner
@@ -41,7 +42,9 @@
import com.android.server.pm.parsing.pkg.AndroidPackage
import com.android.server.pm.test.verify.domain.DomainVerificationTestUtils.mockPackageSettings
import com.android.server.pm.verify.domain.DomainVerificationService
+import com.android.server.testutils.mock
import com.android.server.testutils.mockThrowOnUnmocked
+import com.android.server.testutils.spy
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Test
@@ -49,8 +52,11 @@
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mockito
+import org.mockito.Mockito.doReturn
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
+import java.security.PublicKey
import java.util.UUID
class DomainVerificationPackageTest {
@@ -81,16 +87,16 @@
fun addPackageFirstTime() {
val service = makeService(pkg1, pkg2)
service.addPackage(pkg1)
- val info = service.getInfo(pkg1.getPackageName())
- assertThat(info.packageName).isEqualTo(pkg1.getPackageName())
+ val info = service.getInfo(pkg1.packageName)
+ assertThat(info.packageName).isEqualTo(pkg1.packageName)
assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
assertThat(info.hostToStateMap).containsExactlyEntriesIn(mapOf(
DOMAIN_1 to STATE_NO_RESPONSE,
DOMAIN_2 to STATE_NO_RESPONSE,
))
- val userState = service.getUserState(pkg1.getPackageName())
- assertThat(userState.packageName).isEqualTo(pkg1.getPackageName())
+ val userState = service.getUserState(pkg1.packageName)
+ assertThat(userState.packageName).isEqualTo(pkg1.packageName)
assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
assertThat(userState.user.identifier).isEqualTo(USER_ID)
@@ -100,7 +106,7 @@
))
assertThat(service.queryValidVerificationPackageNames())
- .containsExactly(pkg1.getPackageName())
+ .containsExactly(pkg1.packageName)
}
@Test
@@ -109,14 +115,14 @@
val pkg2 = mockPkgSetting(PKG_TWO, UUID_TWO, SIGNATURE_TWO, isSystemApp = true)
val service = makeService(
- systemConfiguredPackageNames = ArraySet(setOf(pkg1.getPackageName(), pkg2.getPackageName())),
+ systemConfiguredPackageNames = ArraySet(setOf(pkg1.packageName, pkg2.packageName)),
pkg1, pkg2
)
service.addPackage(pkg1)
service.addPackage(pkg2)
- service.getInfo(pkg1.getPackageName()).apply {
- assertThat(packageName).isEqualTo(pkg1.getPackageName())
+ service.getInfo(pkg1.packageName).apply {
+ assertThat(packageName).isEqualTo(pkg1.packageName)
assertThat(identifier).isEqualTo(pkg1.domainSetId)
assertThat(hostToStateMap).containsExactlyEntriesIn(
mapOf(
@@ -126,8 +132,8 @@
)
}
- service.getUserState(pkg1.getPackageName()).apply {
- assertThat(packageName).isEqualTo(pkg1.getPackageName())
+ service.getUserState(pkg1.packageName).apply {
+ assertThat(packageName).isEqualTo(pkg1.packageName)
assertThat(identifier).isEqualTo(pkg1.domainSetId)
assertThat(isLinkHandlingAllowed).isEqualTo(true)
assertThat(user.identifier).isEqualTo(USER_ID)
@@ -139,8 +145,8 @@
)
}
- service.getInfo(pkg2.getPackageName()).apply {
- assertThat(packageName).isEqualTo(pkg2.getPackageName())
+ service.getInfo(pkg2.packageName).apply {
+ assertThat(packageName).isEqualTo(pkg2.packageName)
assertThat(identifier).isEqualTo(pkg2.domainSetId)
assertThat(hostToStateMap).containsExactlyEntriesIn(
mapOf(
@@ -150,8 +156,8 @@
)
}
- service.getUserState(pkg2.getPackageName()).apply {
- assertThat(packageName).isEqualTo(pkg2.getPackageName())
+ service.getUserState(pkg2.packageName).apply {
+ assertThat(packageName).isEqualTo(pkg2.packageName)
assertThat(identifier).isEqualTo(pkg2.domainSetId)
assertThat(isLinkHandlingAllowed).isEqualTo(true)
assertThat(user.identifier).isEqualTo(USER_ID)
@@ -164,7 +170,7 @@
}
assertThat(service.queryValidVerificationPackageNames())
- .containsExactly(pkg1.getPackageName(), pkg2.getPackageName())
+ .containsExactly(pkg1.packageName, pkg2.packageName)
}
@Test
@@ -175,7 +181,7 @@
<domain-verifications>
<active>
<package-state
- packageName="${pkg1.getPackageName()}"
+ packageName="${pkg1.packageName}"
id="${pkg1.domainSetId}"
signature="$DIGEST_ONE"
>
@@ -190,8 +196,8 @@
val service = makeService(pkg1, pkg2)
service.restoreSettings(Xml.resolvePullParser(xml.byteInputStream()))
service.addPackage(pkg1)
- val info = service.getInfo(pkg1.getPackageName())
- assertThat(info.packageName).isEqualTo(pkg1.getPackageName())
+ val info = service.getInfo(pkg1.packageName)
+ assertThat(info.packageName).isEqualTo(pkg1.packageName)
assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
assertThat(info.hostToStateMap).containsExactlyEntriesIn(
mapOf(
@@ -200,8 +206,8 @@
)
)
- val userState = service.getUserState(pkg1.getPackageName())
- assertThat(userState.packageName).isEqualTo(pkg1.getPackageName())
+ val userState = service.getUserState(pkg1.packageName)
+ assertThat(userState.packageName).isEqualTo(pkg1.packageName)
assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
assertThat(userState.user.identifier).isEqualTo(USER_ID)
@@ -213,7 +219,7 @@
)
assertThat(service.queryValidVerificationPackageNames())
- .containsExactly(pkg1.getPackageName())
+ .containsExactly(pkg1.packageName)
}
@Test
@@ -224,7 +230,7 @@
<domain-verifications>
<active>
<package-state
- packageName="${pkg1.getPackageName()}"
+ packageName="${pkg1.packageName}"
id="${pkg1.domainSetId}"
signature="INVALID_SIGNATURE"
>
@@ -239,8 +245,8 @@
val service = makeService(pkg1, pkg2)
service.restoreSettings(Xml.resolvePullParser(xml.byteInputStream()))
service.addPackage(pkg1)
- val info = service.getInfo(pkg1.getPackageName())
- assertThat(info.packageName).isEqualTo(pkg1.getPackageName())
+ val info = service.getInfo(pkg1.packageName)
+ assertThat(info.packageName).isEqualTo(pkg1.packageName)
assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
assertThat(info.hostToStateMap).containsExactlyEntriesIn(
mapOf(
@@ -249,8 +255,8 @@
)
)
- val userState = service.getUserState(pkg1.getPackageName())
- assertThat(userState.packageName).isEqualTo(pkg1.getPackageName())
+ val userState = service.getUserState(pkg1.packageName)
+ assertThat(userState.packageName).isEqualTo(pkg1.packageName)
assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
assertThat(userState.isLinkHandlingAllowed).isEqualTo(true)
assertThat(userState.user.identifier).isEqualTo(USER_ID)
@@ -262,7 +268,7 @@
)
assertThat(service.queryValidVerificationPackageNames())
- .containsExactly(pkg1.getPackageName())
+ .containsExactly(pkg1.packageName)
}
@Test
@@ -273,7 +279,7 @@
<domain-verifications>
<active>
<package-state
- packageName="${pkg1.getPackageName()}"
+ packageName="${pkg1.packageName}"
id="${pkg1.domainSetId}"
>
<state>
@@ -311,8 +317,8 @@
service.addPackage(pkg1)
- val userState = service.getUserState(pkg1.getPackageName())
- assertThat(userState.packageName).isEqualTo(pkg1.getPackageName())
+ val userState = service.getUserState(pkg1.packageName)
+ assertThat(userState.packageName).isEqualTo(pkg1.packageName)
assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
assertThat(userState.isLinkHandlingAllowed).isEqualTo(false)
assertThat(userState.user.identifier).isEqualTo(USER_ID)
@@ -348,7 +354,7 @@
<domain-verifications>
<active>
<package-state
- packageName="${pkg1.getPackageName()}"
+ packageName="${pkg1.packageName}"
id="${pkg1.domainSetId}"
signature="$DIGEST_ONE"
>
@@ -381,8 +387,8 @@
service: DomainVerificationService,
expectRestore: Boolean = false
) {
- val info = service.getInfo(pkg1.getPackageName())
- assertThat(info.packageName).isEqualTo(pkg1.getPackageName())
+ val info = service.getInfo(pkg1.packageName)
+ assertThat(info.packageName).isEqualTo(pkg1.packageName)
assertThat(info.identifier).isEqualTo(pkg1.domainSetId)
assertThat(info.hostToStateMap).containsExactlyEntriesIn(mapOf(
// To share the majority of code, special case restoration to check a different int
@@ -390,8 +396,8 @@
DOMAIN_2 to STATE_NO_RESPONSE,
))
- val userState = service.getUserState(pkg1.getPackageName())
- assertThat(userState.packageName).isEqualTo(pkg1.getPackageName())
+ val userState = service.getUserState(pkg1.packageName)
+ assertThat(userState.packageName).isEqualTo(pkg1.packageName)
assertThat(userState.identifier).isEqualTo(pkg1.domainSetId)
assertThat(userState.isLinkHandlingAllowed).isEqualTo(false)
assertThat(userState.user.identifier).isEqualTo(USER_ID)
@@ -401,10 +407,10 @@
))
assertThat(service.queryValidVerificationPackageNames())
- .containsExactly(pkg1.getPackageName())
+ .containsExactly(pkg1.packageName)
// Re-enable link handling to check that the 3/4 domains were stripped
- service.setDomainVerificationLinkHandlingAllowed(pkg1.getPackageName(), true, USER_ID)
+ service.setDomainVerificationLinkHandlingAllowed(pkg1.packageName, true, USER_ID)
assertThat(service.getOwnersForDomain(DOMAIN_1, USER_ID))
.containsExactly(DomainOwner(PKG_ONE, false))
@@ -630,7 +636,7 @@
serviceBefore.addPackage(pkg2)
serviceBefore.setStatus(pkg1.domainSetId, setOf(DOMAIN_1), STATE_SUCCESS)
- serviceBefore.setDomainVerificationLinkHandlingAllowed(pkg1.getPackageName(), false, 10)
+ serviceBefore.setDomainVerificationLinkHandlingAllowed(pkg1.packageName, false, 10)
serviceBefore.setUserSelection(pkg2.domainSetId, setOf(DOMAIN_2), true, 0)
serviceBefore.setUserSelection(pkg2.domainSetId, setOf(DOMAIN_3), true, 10)
@@ -758,11 +764,11 @@
systemConfiguredPackageNames: ArraySet<String> = ArraySet(),
vararg pkgSettings: PackageSetting
) = makeService(systemConfiguredPackageNames = systemConfiguredPackageNames) {
- pkgName -> pkgSettings.find { pkgName == it.getPackageName() }
+ pkgName -> pkgSettings.find { pkgName == it.packageName }
}
private fun makeService(vararg pkgSettings: PackageSetting) =
- makeService { pkgName -> pkgSettings.find { pkgName == it.getPackageName() } }
+ makeService { pkgName -> pkgSettings.find { pkgName == it.packageName } }
private fun makeService(
systemConfiguredPackageNames: ArraySet<String> = ArraySet(),
@@ -829,14 +835,18 @@
}
whenever(getPkg()) { pkg }
- whenever(getPackageName()) { pkgName }
+ whenever(packageName) { pkgName }
whenever(this.domainSetId) { domainSetId }
whenever(getInstantApp(anyInt())) { false }
whenever(firstInstallTime) { 0L }
whenever(readUserState(0)) { PackageUserState() }
whenever(readUserState(10)) { PackageUserState() }
- whenever(signatures) { arrayOf(Signature(signature)) }
whenever(isSystem) { isSystemApp }
+
+ val mockSigningDetails = SigningDetails(arrayOf(spy(Signature(signature)) {
+ doReturn(mock<PublicKey>()).whenever(this).publicKey
+ }), SigningDetails.SignatureSchemeVersion.UNKNOWN)
+ whenever(signingDetails).thenReturn(mockSigningDetails)
}
private fun DomainVerificationService.assertState(
@@ -845,8 +855,8 @@
linkHandingAllowed: Boolean = true,
hostToStateMap: Map<String, Int>
) {
- getUserState(pkg.getPackageName(), userId).apply {
- assertThat(this.packageName).isEqualTo(pkg.getPackageName())
+ getUserState(pkg.packageName, userId).apply {
+ assertThat(this.packageName).isEqualTo(pkg.packageName)
assertThat(this.identifier).isEqualTo(pkg.domainSetId)
assertThat(this.isLinkHandlingAllowed).isEqualTo(linkHandingAllowed)
assertThat(this.user.identifier).isEqualTo(userId)
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index 942958c..61cd444 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -2898,14 +2898,10 @@
if (idleOptions != null) {
assertEquals(idleOptions, bundleCaptor.getValue());
} else {
- if (isActivity) {
- assertFalse("BAL flag needs to be false in alarm manager",
- bundleCaptor.getValue().getBoolean(
- ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
- true));
- } else {
- assertNull(bundleCaptor.getValue());
- }
+ assertFalse("BAL flag needs to be false in alarm manager",
+ bundleCaptor.getValue().getBoolean(
+ ActivityOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED,
+ true));
}
}
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/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 9d89f12..5b70f77 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -168,8 +168,8 @@
class Mocks {
val lock = PackageManagerTracedLock()
val installLock = Any()
- val injector: PackageManagerService.Injector = mock()
- val systemWrapper: PackageManagerService.SystemWrapper = mock()
+ val injector: PackageManagerServiceInjector = mock()
+ val systemWrapper: PackageManagerServiceInjector.SystemWrapper = mock()
val context: Context = mock()
val userManagerService: UserManagerService = mock()
val componentResolver: ComponentResolver = mock()
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
index 0bce772..9e48045 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/AgentTest.java
@@ -51,6 +51,14 @@
@Mock
private InternalResourceService mIrs;
+ private Scribe mScribe;
+
+ private static class MockScribe extends Scribe {
+ MockScribe(InternalResourceService irs) {
+ super(irs);
+ }
+ }
+
@Before
public void setUp() {
mMockingSession = mockitoSession()
@@ -61,6 +69,7 @@
when(mIrs.getContext()).thenReturn(mContext);
when(mIrs.getCompleteEconomicPolicyLocked()).thenReturn(mEconomicPolicy);
when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mock(AlarmManager.class));
+ mScribe = new MockScribe(mIrs);
}
@After
@@ -72,7 +81,7 @@
@Test
public void testRecordTransaction_UnderMax() {
- Agent agent = new Agent(mIrs);
+ Agent agent = new Agent(mIrs, mScribe);
Ledger ledger = new Ledger();
doReturn(1_000_000L).when(mIrs).getMaxCirculationLocked();
@@ -101,7 +110,7 @@
@Test
public void testRecordTransaction_MaxCirculation() {
- Agent agent = new Agent(mIrs);
+ Agent agent = new Agent(mIrs, mScribe);
Ledger ledger = new Ledger();
doReturn(1000L).when(mIrs).getMaxCirculationLocked();
@@ -148,7 +157,7 @@
@Test
public void testRecordTransaction_MaxSatiatedBalance() {
- Agent agent = new Agent(mIrs);
+ Agent agent = new Agent(mIrs, mScribe);
Ledger ledger = new Ledger();
doReturn(1_000_000L).when(mIrs).getMaxCirculationLocked();
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index ef9248a..8848098 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -53,6 +53,7 @@
"testng",
"ub-uiautomator",
"platformprotosnano",
+ "framework-protos",
"hamcrest-library",
"servicestests-utils",
"service-appsearch",
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index fe4fed9..fb5c557 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -1138,7 +1138,7 @@
public void testSetScale_toMagnifying_shouldNotifyActivatedState() {
setScaleToMagnifying();
- verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(true));
+ verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(DISPLAY_0), eq(true));
}
@Test
@@ -1146,7 +1146,7 @@
setScaleToMagnifying();
mFullScreenMagnificationController.reset(DISPLAY_0, mAnimationCallback);
- verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(false));
+ verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(DISPLAY_0), eq(false));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index b14c353..6c32f7e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -42,7 +42,6 @@
import android.graphics.PointF;
import android.os.Handler;
import android.os.Message;
-import android.provider.Settings;
import android.util.DebugUtils;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -458,14 +457,6 @@
verify(mWindowMagnificationPromptController).showNotificationIfNeeded();
}
- @Test
- public void testZoomedWithTripleTap_callsOnTripleTapped() {
- goFromStateIdleTo(STATE_ZOOMED_2TAPS);
-
- verify(mMockCallback).onTripleTapped(DISPLAY_0,
- Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
- }
-
private void assertActionsInOrder(List<MotionEvent> actualEvents,
List<Integer> expectedActions) {
assertTrue(actualEvents.size() == expectedActions.size());
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index e82adc8..2cb3d27 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -380,9 +380,9 @@
@Test
public void
onFullScreenMagnificationActivationState_fullScreenActivated_logFullScreenDuration() {
- mMagnificationController.onFullScreenMagnificationActivationState(true);
+ mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
- mMagnificationController.onFullScreenMagnificationActivationState(false);
+ mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, false);
verify(mMagnificationController).logMagnificationUsageState(
eq(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN), anyLong());
@@ -460,75 +460,47 @@
}
@Test
- public void onShortcutTriggered_windowModeEnabledAndCapabilitiesAll_showMagnificationButton()
+ public void onWindowActivated_windowModeEnabledAndCapabilitiesAll_showMagnificationButton()
throws RemoteException {
setMagnificationEnabled(MODE_WINDOW);
- mMagnificationController.onShortcutTriggered(TEST_DISPLAY, MODE_WINDOW);
+ mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, true);
verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_WINDOW));
}
@Test
- public void onShortcutTriggered_fullscreenEnabledAndCapabilitiesAll_showMagnificationButton()
+ public void onFullScreenActivated_fullscreenEnabledAndCapabilitiesAll_showMagnificationButton()
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
- mMagnificationController.onShortcutTriggered(TEST_DISPLAY, MODE_FULLSCREEN);
+ mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
eq(MODE_FULLSCREEN));
}
@Test
- public void triggerShortcutToShowMagnificationBound_fullscreenMode_showMagnificationButton() {
- setMagnificationModeSettings(MODE_FULLSCREEN);
-
- when(mScreenMagnificationController.isForceShowMagnifiableBounds(TEST_DISPLAY)).thenReturn(
- true);
- mMagnificationController.onShortcutTriggered(TEST_DISPLAY, MODE_FULLSCREEN);
-
- verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
- eq(MODE_FULLSCREEN));
- }
-
- @Test
- public void onShortcutTriggered_windowModeDisabled_removeMagnificationButton()
+ public void onWindowDeactivated_windowModeInactive_removeMagnificationButton()
throws RemoteException {
+ setMagnificationEnabled(MODE_WINDOW);
+ mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
- mMagnificationController.onShortcutTriggered(TEST_DISPLAY, MODE_WINDOW);
+ mMagnificationController.onWindowMagnificationActivationState(TEST_DISPLAY, false);
verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
}
@Test
- public void onTripleTap_windowModeEnabledAndCapabilitiesAll_showMagnificationButton()
- throws RemoteException {
- setMagnificationEnabled(MODE_WINDOW);
-
- mMagnificationController.onTripleTapped(TEST_DISPLAY, MODE_WINDOW);
-
- verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
- eq(MODE_WINDOW));
- }
-
- @Test
- public void onTripleTap_fullscreenEnabledAndCapabilitiesAll_showMagnificationButton()
+ public void onFullScreenDeactivated_fullscreenModeInactive_removeMagnificationButton()
throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
+ mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY,
+ /* scale= */1, MAGNIFIED_CENTER_X, MAGNIFIED_CENTER_Y,
+ true, TEST_SERVICE_ID);
- mMagnificationController.onTripleTapped(TEST_DISPLAY, MODE_FULLSCREEN);
-
- verify(mWindowMagnificationManager).showMagnificationButton(eq(TEST_DISPLAY),
- eq(MODE_FULLSCREEN));
- }
-
- @Test
- public void onTripleTap_windowModeDisabled_removeMagnificationButton()
- throws RemoteException {
-
- mMagnificationController.onTripleTapped(TEST_DISPLAY, MODE_WINDOW);
+ mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, false);
verify(mWindowMagnificationManager).removeMagnificationButton(eq(TEST_DISPLAY));
}
@@ -574,7 +546,7 @@
@Test
public void imeWindowStateShown_fullScreenMagnifying_logFullScreenMode() {
- mMagnificationController.onFullScreenMagnificationActivationState(true);
+ mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
mMagnificationController.onImeWindowVisibilityChanged(true);
@@ -591,7 +563,7 @@
@Test
public void imeWindowStateHidden_windowMagnifying_noLogAnyMode() {
- mMagnificationController.onFullScreenMagnificationActivationState(true);
+ mMagnificationController.onFullScreenMagnificationActivationState(TEST_DISPLAY, true);
verify(mMagnificationController, never()).logMagnificationModeWithIme(anyInt());
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
index ef6ed88..d301621 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationGestureHandlerTest.java
@@ -121,14 +121,6 @@
}
}
-
- @Test
- public void notifyShortcutTriggered_callsOnShortcutTriggered() {
- mMgh.notifyShortcutTriggered();
-
- verify(mCallback).onShortcutTriggered(eq(DISPLAY_0), eq(mMgh.getMode()));
- }
-
private static class TestMagnificationGestureHandler extends MagnificationGestureHandler {
boolean mIsInternalMethodCalled = false;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index 6a5aae6..95f4327 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -19,9 +19,7 @@
import static com.android.server.testutils.TestUtils.strictMock;
import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
import android.content.Context;
import android.graphics.PointF;
@@ -150,14 +148,6 @@
});
}
- @Test
- public void onTripleTap_callsOnTripleTapped() {
- goFromStateIdleTo(STATE_SHOW_MAGNIFIER_TRIPLE_TAP);
-
- verify(mMockCallback).onTripleTapped(eq(DISPLAY_0),
- eq(mWindowMagnificationGestureHandler.getMode()));
- }
-
private void forEachState(IntConsumer action) {
for (int state = FIRST_STATE; state <= LAST_STATE; state++) {
action.accept(state);
diff --git a/services/tests/servicestests/src/com/android/server/am/CriticalEventLogTest.java b/services/tests/servicestests/src/com/android/server/am/CriticalEventLogTest.java
new file mode 100644
index 0000000..903d7f2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/CriticalEventLogTest.java
@@ -0,0 +1,475 @@
+/*
+ * 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.am;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.android.framework.protobuf.nano.MessageNano;
+import com.android.server.am.CriticalEventLog.ILogLoader;
+import com.android.server.am.CriticalEventLog.LogLoader;
+import com.android.server.am.nano.CriticalEventLogProto;
+import com.android.server.am.nano.CriticalEventLogStorageProto;
+import com.android.server.am.nano.CriticalEventProto;
+import com.android.server.am.nano.CriticalEventProto.HalfWatchdog;
+import com.android.server.am.nano.CriticalEventProto.Watchdog;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.UUID;
+
+/**
+ * Test class for {@link CriticalEventLog}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:CriticalEventLogTest
+ */
+public class CriticalEventLogTest {
+ /** Epoch time when the critical event log is instantiated. */
+ private static final long START_TIME_MS = 1577880000000L; // 2020-01-01 12:00:00.000 UTC
+
+ /** Max number of events to include in the log. */
+ private static final int BUFFER_CAPACITY = 5;
+
+ /** Max age of events to include in the output log proto. */
+ private static final Duration LOG_WINDOW = Duration.ofMinutes(5);
+
+ /** How long to wait between consecutive saves of the log to disk. */
+ private static final Duration MIN_TIME_BETWEEN_SAVES = Duration.ofSeconds(2);
+
+ private static final String UUID_STRING = "123e4567-e89b-12d3-a456-556642440000";
+
+ @Rule
+ public TemporaryFolder mFolder = new TemporaryFolder();
+
+ private TestableCriticalEventLog mCriticalEventLog;
+ private File mTestFile;
+
+ @Before
+ public void setup() throws IOException {
+ mTestFile = mFolder.newFile(CriticalEventLog.FILENAME);
+ setLogInstance();
+ }
+
+ @Test
+ public void loadEvents_validContents() throws Exception {
+ createTestFileWithEvents(2);
+ setLogInstance(); // Log instance reads the proto file at initialization.
+
+ CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
+ assertProtoArrayEquals(
+ logProto.events,
+ new CriticalEventProto[]{
+ watchdog(START_TIME_MS - 2000, "Old watchdog 1"),
+ watchdog(START_TIME_MS - 1000, "Old watchdog 2"),
+ });
+ }
+
+ @Test
+ public void loadEvents_fileDoesntExist() {
+ mTestFile.delete();
+ setLogInstance();
+
+ CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
+ assertThat(logProto.events).isEmpty();
+ }
+
+ @Test
+ public void loadEvents_directoryDoesntExist() {
+ mFolder.delete();
+ setLogInstance();
+
+ CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
+ assertThat(logProto.events).isEmpty();
+ }
+
+ @Test
+ public void loadEvents_unreadable() throws Exception {
+ createTestFileWithEvents(1);
+ mTestFile.setReadable(false);
+ setLogInstance();
+
+ CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
+ assertThat(logProto.events).isEmpty();
+ }
+
+ @Test
+ public void loadEvents_malformedFile() throws Exception {
+ try (FileOutputStream stream = new FileOutputStream(mTestFile)) {
+ stream.write("This is not a proto file.".getBytes(StandardCharsets.UTF_8));
+ }
+ setLogInstance();
+
+ CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
+ assertThat(logProto.events).isEmpty();
+ }
+
+ @Test
+ public void loadEvents_emptyProto() throws Exception {
+ createTestFileWithEvents(0);
+ setLogInstance();
+
+ CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
+ assertThat(logProto.events).isEmpty();
+ }
+
+ @Test
+ public void loadEvents_numEventsExceedsBufferCapacity() throws Exception {
+ createTestFileWithEvents(10); // Ring buffer capacity is 5
+ setLogInstance();
+
+ CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
+ // Log contains the last 5 events only.
+ assertProtoArrayEquals(
+ logProto.events,
+ new CriticalEventProto[]{
+ watchdog(START_TIME_MS - 5000, "Old watchdog 6"),
+ watchdog(START_TIME_MS - 4000, "Old watchdog 7"),
+ watchdog(START_TIME_MS - 3000, "Old watchdog 8"),
+ watchdog(START_TIME_MS - 2000, "Old watchdog 9"),
+ watchdog(START_TIME_MS - 1000, "Old watchdog 10"),
+ });
+ }
+
+ @Test
+ public void logLinesForAnrFile() {
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logWatchdog("Watchdog subject",
+ UUID.fromString("123e4567-e89b-12d3-a456-556642440000"));
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logHalfWatchdog("Half watchdog subject");
+ mCriticalEventLog.incTimeSeconds(1);
+
+ assertThat(mCriticalEventLog.logLinesForAnrFile()).isEqualTo(
+ "--- CriticalEventLog ---\n"
+ + "capacity: 5\n"
+ + "events <\n"
+ + " timestamp_ms: 1577880001000\n"
+ + " watchdog <\n"
+ + " subject: \"Watchdog subject\"\n"
+ + " uuid: \"123e4567-e89b-12d3-a456-556642440000\"\n"
+ + " >\n"
+ + ">\n"
+ + "events <\n"
+ + " timestamp_ms: 1577880002000\n"
+ + " half_watchdog <\n"
+ + " subject: \"Half watchdog subject\"\n"
+ + " >\n"
+ + ">\n"
+ + "timestamp_ms: 1577880003000\n"
+ + "window_ms: 300000\n\n");
+ }
+
+ @Test
+ public void logWatchdog() {
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logWatchdog("Subject 1",
+ UUID.fromString("123e4567-e89b-12d3-a456-556642440000"));
+ mCriticalEventLog.incTimeSeconds(1);
+
+ CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
+ assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
+ watchdog(START_TIME_MS + 1000, "Subject 1", "123e4567-e89b-12d3-a456-556642440000")
+ });
+ }
+
+ @Test
+ public void logHalfWatchdog() {
+ setLogInstance();
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logHalfWatchdog("Subject 1");
+ mCriticalEventLog.incTimeSeconds(1);
+
+ CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 2000);
+ assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
+ halfWatchdog(START_TIME_MS + 1000, "Subject 1")
+ });
+ }
+
+ @Test
+ public void getOutputLogProto_numberOfEventsExceedsCapacity() {
+ // Log 10 events in 10 sec (capacity = 5)
+ for (int i = 0; i < 10; i++) {
+ mCriticalEventLog.logWatchdog("Subject " + i,
+ UUID.fromString(UUID_STRING));
+ mCriticalEventLog.incTimeSeconds(1);
+ }
+
+ CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 10000);
+ assertThat(logProto.windowMs).isEqualTo(300_000); // 5 minutes
+ assertThat(logProto.capacity).isEqualTo(5);
+
+ // Only last 5 events are included in log output.
+ assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
+ watchdog(START_TIME_MS + 5000, "Subject 5", UUID_STRING),
+ watchdog(START_TIME_MS + 6000, "Subject 6", UUID_STRING),
+ watchdog(START_TIME_MS + 7000, "Subject 7", UUID_STRING),
+ watchdog(START_TIME_MS + 8000, "Subject 8", UUID_STRING),
+ watchdog(START_TIME_MS + 9000, "Subject 9", UUID_STRING),
+ });
+ }
+
+ @Test
+ public void getOutputLogProto_logContainsOldEvents() {
+ long logTimestamp = START_TIME_MS + Duration.ofDays(1).toMillis();
+
+ // Old events (older than 5 mins)
+ mCriticalEventLog.setCurrentTimeMillis(logTimestamp - Duration.ofSeconds(302).toMillis());
+ mCriticalEventLog.logHalfWatchdog("Old event 1"); // 5m2s old
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logHalfWatchdog("Old event 2"); // 5m1s old
+ mCriticalEventLog.incTimeSeconds(1);
+
+ // New events (5 mins old or less)
+ mCriticalEventLog.logHalfWatchdog("New event 1"); // 5m0s old
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logHalfWatchdog("New event 2"); // 5m59s old
+
+ mCriticalEventLog.setCurrentTimeMillis(logTimestamp);
+ CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+
+ assertThat(logProto.timestampMs).isEqualTo(logTimestamp);
+ assertThat(logProto.windowMs).isEqualTo(300_000); // 5 minutes
+ assertThat(logProto.capacity).isEqualTo(5);
+
+ // Only events with age <= 5 min are included
+ assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
+ halfWatchdog(logTimestamp - Duration.ofSeconds(300).toMillis(), "New event 1"),
+ halfWatchdog(logTimestamp - Duration.ofSeconds(299).toMillis(), "New event 2"),
+ });
+ }
+
+ @Test
+ public void getOutputLogProto_logHasNotBeenLoadedFromDiskYet() throws Exception {
+ createTestFileWithEvents(5);
+ setLogInstance(new NoOpLogLoader());
+
+ CriticalEventLogProto logProto = mCriticalEventLog.getRecentEvents();
+
+ // Output log is empty.
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS);
+ assertThat(logProto.events).isEmpty();
+ }
+
+ @Test
+ public void saveEventsToDiskNow() throws Exception {
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logWatchdog("Watchdog subject", UUID.fromString(UUID_STRING));
+
+ mCriticalEventLog.incTimeSeconds(1);
+ mCriticalEventLog.logHalfWatchdog("Half watchdog subject");
+
+ // Don't need to call saveEventsToDiskNow since it's called after every event
+ // when mSaveImmediately = true.
+
+ CriticalEventLogStorageProto expected = new CriticalEventLogStorageProto();
+ expected.events = new CriticalEventProto[]{
+ watchdog(START_TIME_MS + 1000, "Watchdog subject", UUID_STRING),
+ halfWatchdog(START_TIME_MS + 2000, "Half watchdog subject")
+ };
+
+ assertThat(MessageNano.messageNanoEquals(getEventsWritten(), expected)).isTrue();
+ }
+
+ @Test
+ public void saveDelayMs() {
+ // First save has no delay
+ assertThat(mCriticalEventLog.saveDelayMs()).isEqualTo(0L);
+
+ // Save log, then next save delay is in 2s
+ mCriticalEventLog.saveLogToFileNow();
+ assertThat(mCriticalEventLog.saveDelayMs()).isEqualTo(2000L);
+ mCriticalEventLog.incTimeSeconds(1);
+ assertThat(mCriticalEventLog.saveDelayMs()).isEqualTo(1000L);
+
+ // Save again, save delay is 2s again.
+ mCriticalEventLog.saveLogToFileNow();
+ assertThat(mCriticalEventLog.saveDelayMs()).isEqualTo(2000L);
+ }
+
+ @Test
+ public void simulateReboot_saveAndLoadCycle() {
+ TestableCriticalEventLog log1 = setLogInstance();
+
+ // Log 8 events
+ for (int i = 0; i < 8; i++) {
+ log1.logHalfWatchdog("Old subject " + i);
+ log1.incTimeSeconds(1);
+ }
+
+ // Simulate reboot by making new log instance.
+ TestableCriticalEventLog log2 = setLogInstance();
+ assertThat(log1).isNotSameInstanceAs(log2);
+
+ // Log one more event
+ log2.setCurrentTimeMillis(START_TIME_MS + 20_000);
+ log2.logHalfWatchdog("New subject");
+ log2.incTimeSeconds(1);
+
+ CriticalEventLogProto logProto = log2.getRecentEvents();
+
+ // Log contains 4 + 1 events.
+ assertThat(logProto.timestampMs).isEqualTo(START_TIME_MS + 21_000);
+ assertProtoArrayEquals(logProto.events, new CriticalEventProto[]{
+ halfWatchdog(START_TIME_MS + 4000, "Old subject 4"),
+ halfWatchdog(START_TIME_MS + 5000, "Old subject 5"),
+ halfWatchdog(START_TIME_MS + 6000, "Old subject 6"),
+ halfWatchdog(START_TIME_MS + 7000, "Old subject 7"),
+ halfWatchdog(START_TIME_MS + 20_000, "New subject")
+ });
+ }
+
+ private CriticalEventLogStorageProto getEventsWritten() throws IOException {
+ return CriticalEventLogStorageProto.parseFrom(
+ Files.readAllBytes(mTestFile.toPath()));
+ }
+
+ /**
+ * Creates a log file containing some watchdog events.
+ *
+ * They occur at a rate of one per second, with the last at 1 sec before START_TIME_MS.
+ */
+ private void createTestFileWithEvents(int numEvents) throws Exception {
+ CriticalEventLogStorageProto log = new CriticalEventLogStorageProto();
+ log.events = new CriticalEventProto[numEvents];
+ long startTimeMs = START_TIME_MS - (numEvents * 1000L);
+
+ for (int i = 0; i < numEvents; i++) {
+ long timestampMs = startTimeMs + (i * 1000L);
+ String subject = String.format("Old watchdog %d", i + 1);
+ log.events[i] = watchdog(timestampMs, subject);
+ }
+
+ try (FileOutputStream stream = new FileOutputStream(mTestFile)) {
+ stream.write(CriticalEventLogStorageProto.toByteArray(log));
+ }
+ }
+
+ private CriticalEventProto watchdog(long timestampMs, String subject) {
+ return watchdog(timestampMs, subject, "A UUID");
+ }
+
+ private CriticalEventProto watchdog(long timestampMs, String subject, String uuid) {
+ CriticalEventProto event = new CriticalEventProto();
+ event.timestampMs = timestampMs;
+ event.setWatchdog(new Watchdog());
+ event.getWatchdog().subject = subject;
+ event.getWatchdog().uuid = uuid;
+ return event;
+ }
+
+ private CriticalEventProto halfWatchdog(long timestampMs, String subject) {
+ CriticalEventProto event = new CriticalEventProto();
+ event.timestampMs = timestampMs;
+ event.setHalfWatchdog(new HalfWatchdog());
+ event.getHalfWatchdog().subject = subject;
+ return event;
+ }
+
+ private static void assertProtoArrayEquals(MessageNano[] actual, MessageNano[] expected) {
+ assertThat(expected).isNotNull();
+ assertThat(actual).isNotNull();
+
+ String message =
+ "Expected:\n" + Arrays.toString(expected) + "\nGot:\n" + Arrays.toString(actual);
+ assertWithMessage(message).that(expected.length).isEqualTo(actual.length);
+ for (int i = 0; i < expected.length; i++) {
+ assertWithMessage(message).that(
+ MessageNano.messageNanoEquals(expected[i], actual[i])).isTrue();
+ }
+ }
+
+ private TestableCriticalEventLog setLogInstance() {
+ return setLogInstance(new LogLoader());
+ }
+
+ private TestableCriticalEventLog setLogInstance(ILogLoader logLoader) {
+ mCriticalEventLog = new TestableCriticalEventLog(mFolder.getRoot().getAbsolutePath(),
+ logLoader);
+ return mCriticalEventLog;
+ }
+
+ private static class TestableCriticalEventLog extends CriticalEventLog {
+ private long mNowMillis = START_TIME_MS;
+
+ TestableCriticalEventLog(String logDir, ILogLoader logLoader) {
+ super(logDir,
+ BUFFER_CAPACITY,
+ (int) LOG_WINDOW.toMillis(),
+ MIN_TIME_BETWEEN_SAVES.toMillis(),
+ /* loadAndSaveImmediately= */ true,
+ logLoader);
+ }
+
+ @Override
+ protected long getWallTimeMillis() {
+ return mNowMillis;
+ }
+
+ void incTimeSeconds(int seconds) {
+ mNowMillis += (seconds * 1000L);
+ }
+
+ void setCurrentTimeMillis(long millis) {
+ mNowMillis = millis;
+ }
+ }
+
+ /**
+ * A log loader that does nothing.
+ *
+ * Used to check behaviour when log loading is slow since the loading happens
+ * asynchronously.
+ */
+ private static class NoOpLogLoader implements ILogLoader {
+ @Override
+ public void load(File logFile,
+ CriticalEventLog.ThreadSafeRingBuffer<CriticalEventProto> buffer) {
+ // Do nothing.
+ }
+ }
+}
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 91bf4d1..0000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/AppSearchImplPlatformTest.java
+++ /dev/null
@@ -1,463 +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,
- /*setSchemaStatsBuilder=*/ null);
-
- // "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,
- /*setSchemaStatsBuilder=*/ null);
-
- // 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,
- /*setSchemaStatsBuilder=*/ null);
-
- // "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,
- /*setSchemaStatsBuilder=*/ null);
-
- // 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,
- /*setSchemaStatsBuilder=*/ null);
-
- 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,
- /*setSchemaStatsBuilder=*/ null);
-
- 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,
- /*setSchemaStatsBuilder=*/ null);
-
- 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,
- /*setSchemaStatsBuilder=*/ null);
- 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,
- /*setSchemaStatsBuilder=*/ null);
- 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/UserStorageInfoTest.java b/services/tests/servicestests/src/com/android/server/appsearch/UserStorageInfoTest.java
deleted file mode 100644
index c79671f..0000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/UserStorageInfoTest.java
+++ /dev/null
@@ -1,123 +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.server.appsearch.UserStorageInfo.STORAGE_INFO_FILE;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.server.appsearch.icing.proto.DocumentStorageInfoProto;
-import com.android.server.appsearch.icing.proto.NamespaceStorageInfoProto;
-import com.android.server.appsearch.icing.proto.StorageInfoProto;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-public class UserStorageInfoTest {
- @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
- private File mFileParentPath;
- private UserStorageInfo mUserStorageInfo;
-
- @Before
- public void setUp() throws Exception {
- mFileParentPath = mTemporaryFolder.newFolder();
- mUserStorageInfo = new UserStorageInfo(mFileParentPath);
- }
-
- @Test
- public void testReadStorageInfoFromFile() throws IOException {
- NamespaceStorageInfoProto namespaceStorageInfo1 =
- NamespaceStorageInfoProto.newBuilder()
- .setNamespace("pkg1$db/namespace")
- .setNumAliveDocuments(2)
- .setNumExpiredDocuments(1)
- .build();
- NamespaceStorageInfoProto namespaceStorageInfo2 =
- NamespaceStorageInfoProto.newBuilder()
- .setNamespace("pkg2$db/namespace")
- .setNumAliveDocuments(3)
- .setNumExpiredDocuments(3)
- .build();
- DocumentStorageInfoProto documentStorageInfo =
- DocumentStorageInfoProto.newBuilder()
- .setNumAliveDocuments(5)
- .setNumExpiredDocuments(4)
- .addNamespaceStorageInfo(namespaceStorageInfo1)
- .addNamespaceStorageInfo(namespaceStorageInfo2)
- .build();
- StorageInfoProto storageInfo =
- StorageInfoProto.newBuilder()
- .setDocumentStorageInfo(documentStorageInfo)
- .setTotalStorageSize(9)
- .build();
- File storageInfoFile = new File(mFileParentPath, STORAGE_INFO_FILE);
- try (FileOutputStream out = new FileOutputStream(storageInfoFile)) {
- storageInfo.writeTo(out);
- }
-
- mUserStorageInfo.readStorageInfoFromFile();
-
- assertThat(mUserStorageInfo.getTotalSizeBytes()).isEqualTo(
- storageInfo.getTotalStorageSize());
- // We calculate the package storage size based on number of documents a package has.
- // Here, total storage size is 9. pkg1 has 3 docs and pkg2 has 6 docs. So storage size of
- // pkg1 is 3. pkg2's storage size is 6.
- assertThat(mUserStorageInfo.getSizeBytesForPackage("pkg1")).isEqualTo(3);
- assertThat(mUserStorageInfo.getSizeBytesForPackage("pkg2")).isEqualTo(6);
- assertThat(mUserStorageInfo.getSizeBytesForPackage("invalid_pkg")).isEqualTo(0);
- }
-
- @Test
- public void testCalculatePackageStorageInfoMap() {
- NamespaceStorageInfoProto namespaceStorageInfo1 =
- NamespaceStorageInfoProto.newBuilder()
- .setNamespace("pkg1$db/namespace")
- .setNumAliveDocuments(2)
- .setNumExpiredDocuments(1)
- .build();
- NamespaceStorageInfoProto namespaceStorageInfo2 =
- NamespaceStorageInfoProto.newBuilder()
- .setNamespace("pkg2$db/namespace")
- .setNumAliveDocuments(3)
- .setNumExpiredDocuments(3)
- .build();
- DocumentStorageInfoProto documentStorageInfo =
- DocumentStorageInfoProto.newBuilder()
- .setNumAliveDocuments(5)
- .setNumExpiredDocuments(4)
- .addNamespaceStorageInfo(namespaceStorageInfo1)
- .addNamespaceStorageInfo(namespaceStorageInfo2)
- .build();
- StorageInfoProto storageInfo =
- StorageInfoProto.newBuilder()
- .setDocumentStorageInfo(documentStorageInfo)
- .setTotalStorageSize(9)
- .build();
-
- // We calculate the package storage size based on number of documents a package has.
- // Here, total storage size is 9. pkg1 has 3 docs and pkg2 has 6 docs. So storage size of
- // pkg1 is 3. pkg2's storage size is 6.
- assertThat(mUserStorageInfo.calculatePackageStorageInfoMap(storageInfo))
- .containsExactly("pkg1", 3L, "pkg2", 6L);
- }
-}
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 dd3b3ec..0000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchImplTest.java
+++ /dev/null
@@ -1,3346 +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 {
- /**
- * 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 AppSearchImpl mAppSearchImpl;
-
- @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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "package",
- "database2",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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, /*statsBuilder=*/ null);
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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, /*statsBuilder=*/ null));
- 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, /*statsBuilder=*/ null);
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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, /*statsBuilder=*/ null);
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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, /*statsBuilder=*/ null));
- 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, /*statsBuilder=*/ null);
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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, /*statsBuilder=*/ null));
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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, /*statsBuilder=*/ null);
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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, /*statsBuilder=*/ null));
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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, /*statsBuilder=*/ null);
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "package",
- "database2",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "packageB",
- "database",
- schema,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
- 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,
- /* setSchemaStatsBuilder= */ null);
- 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,
- /* setSchemaStatsBuilder= */ null);
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // "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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "package1",
- "database2",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- 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,
- /* setSchemaStatsBuilder= */ null));
-
- 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, /*statsBuilder=*/ null));
-
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "package1",
- "database2",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "package2",
- "database1",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
- mAppSearchImpl.setSchema(
- "package2",
- "database2",
- schemas,
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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 2ab5fd5..0000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/AppSearchLoggerTest.java
+++ /dev/null
@@ -1,906 +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.external.localstorage.stats.SetSchemaStats;
-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.SetSchemaResultProto;
-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 {
- private static final String PACKAGE_NAME = "packageName";
- private static final String DATABASE = "database";
- /**
- * 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 AppSearchImpl mAppSearchImpl;
- private TestLogger mLogger;
-
- @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;
- @Nullable SetSchemaStats mSetSchemaStats;
-
- @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;
- }
-
- @Override
- public void logStats(@NonNull SetSchemaStats stats) {
- mSetSchemaStats = 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(PACKAGE_NAME, 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, PACKAGE_NAME)
- .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);
- }
-
- @Test
- public void testAppSearchLoggerHelper_testCopyNativeStats_setSchema() {
- ImmutableList<String> newSchemaTypeChangeList = ImmutableList.of("new1");
- ImmutableList<String> deletedSchemaTypesList = ImmutableList.of("deleted1", "deleted2");
- ImmutableList<String> compatibleTypesList = ImmutableList.of("compatible1", "compatible2");
- ImmutableList<String> indexIncompatibleTypeChangeList = ImmutableList.of("index1");
- ImmutableList<String> backwardsIncompatibleTypeChangeList = ImmutableList.of("backwards1");
- SetSchemaResultProto setSchemaResultProto =
- SetSchemaResultProto.newBuilder()
- .addAllNewSchemaTypes(newSchemaTypeChangeList)
- .addAllDeletedSchemaTypes(deletedSchemaTypesList)
- .addAllFullyCompatibleChangedSchemaTypes(compatibleTypesList)
- .addAllIndexIncompatibleChangedSchemaTypes(indexIncompatibleTypeChangeList)
- .addAllIncompatibleSchemaTypes(backwardsIncompatibleTypeChangeList)
- .build();
- SetSchemaStats.Builder sBuilder = new SetSchemaStats.Builder(PACKAGE_NAME, DATABASE);
-
- AppSearchLoggerHelper.copyNativeStats(setSchemaResultProto, sBuilder);
-
- SetSchemaStats sStats = sBuilder.build();
- assertThat(sStats.getNewTypeCount()).isEqualTo(newSchemaTypeChangeList.size());
- assertThat(sStats.getDeletedTypeCount()).isEqualTo(deletedSchemaTypesList.size());
- assertThat(sStats.getCompatibleTypeChangeCount()).isEqualTo(compatibleTypesList.size());
- assertThat(sStats.getIndexIncompatibleTypeChangeCount())
- .isEqualTo(indexIncompatibleTypeChangeList.size());
- assertThat(sStats.getBackwardsIncompatibleTypeChangeCount())
- .isEqualTo(backwardsIncompatibleTypeChangeList.size());
- }
-
- //
- // 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,
- /* setSchemaStatsBuilder= */ null);
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- // 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,
- /* setSchemaStatsBuilder= */ null);
-
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- 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,
- /* setSchemaStatsBuilder= */ null);
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- 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,
- /* setSchemaStatsBuilder= */ null);
- 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,
- /* setSchemaStatsBuilder= */ null);
-
- 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,
- /* setSchemaStatsBuilder= */ null);
- 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.DEPRECATED_QUERY_VALUE);
- assertThat(rStats.getDeletedDocumentCount()).isEqualTo(2);
- }
-
- @Test
- public void testLoggingStats_setSchema() throws Exception {
- AppSearchSchema schema1 =
- new AppSearchSchema.Builder("testSchema")
- .addProperty(
- new AppSearchSchema.StringPropertyConfig.Builder("subject")
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_REQUIRED)
- .setIndexingType(
- AppSearchSchema.StringPropertyConfig
- .INDEXING_TYPE_PREFIXES)
- .setTokenizerType(
- AppSearchSchema.StringPropertyConfig
- .TOKENIZER_TYPE_PLAIN)
- .build())
- .build();
- mAppSearchImpl.setSchema(
- PACKAGE_NAME,
- DATABASE,
- Collections.singletonList(schema1),
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ null);
-
- // create a backwards incompatible schema
- SetSchemaStats.Builder sStatsBuilder = new SetSchemaStats.Builder(PACKAGE_NAME, DATABASE);
- AppSearchSchema schema2 = new AppSearchSchema.Builder("testSchema").build();
- mAppSearchImpl.setSchema(
- PACKAGE_NAME,
- DATABASE,
- Collections.singletonList(schema2),
- /*visibilityStore=*/ null,
- /*schemasNotDisplayedBySystem=*/ Collections.emptyList(),
- /*schemasVisibleToPackages=*/ Collections.emptyMap(),
- /*forceOverride=*/ false,
- /*version=*/ 0,
- /* setSchemaStatsBuilder= */ sStatsBuilder);
-
- SetSchemaStats sStats = sStatsBuilder.build();
- assertThat(sStats.getPackageName()).isEqualTo(PACKAGE_NAME);
- assertThat(sStats.getDatabase()).isEqualTo(DATABASE);
- assertThat(sStats.getNewTypeCount()).isEqualTo(0);
- assertThat(sStats.getCompatibleTypeChangeCount()).isEqualTo(0);
- assertThat(sStats.getIndexIncompatibleTypeChangeCount()).isEqualTo(1);
- assertThat(sStats.getBackwardsIncompatibleTypeChangeCount()).isEqualTo(1);
- }
-}
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 81aab41..0000000
--- a/services/tests/servicestests/src/com/android/server/appsearch/external/localstorage/stats/AppSearchStatsTest.java
+++ /dev/null
@@ -1,393 +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 newTypeCount = 1;
- int compatibleTypeChangeCount = 2;
- int indexIncompatibleTypeChangeCount = 3;
- int backwardsIncompatibleTypeChangeCount = 4;
- SetSchemaStats sStats =
- new SetSchemaStats.Builder(TEST_PACKAGE_NAME, TEST_DATA_BASE)
- .setStatusCode(TEST_STATUS_CODE)
- .setSchemaMigrationStats(schemaMigrationStats)
- .setTotalLatencyMillis(TEST_TOTAL_LATENCY_MILLIS)
- .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.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;
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 140a8eb..10d1e0e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1080,7 +1080,7 @@
dpm.setActiveAdmin(admin1, /* replace =*/ false);
// Fire!
- assertThat(dpm.setDeviceOwner(admin1, "owner-name")).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
// getDeviceOwnerComponent should return the admin1 component.
assertThat(dpm.getDeviceOwnerComponentOnCallingUser()).isEqualTo(admin1);
@@ -1311,7 +1311,8 @@
assertExpectException(IllegalArgumentException.class,
/* messageRegex= */ "Invalid component",
- () -> dpm.setDeviceOwner(new ComponentName("a.b.c", ".def")));
+ () -> dpm.setDeviceOwner(new ComponentName("a.b.c", ".def"), /* ownerName= */ null,
+ UserHandle.USER_SYSTEM));
}
@Test
@@ -1346,7 +1347,7 @@
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name")).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
// Verify internal calls.
verify(getServices().iactivityManager, times(1)).updateDeviceOwner(
@@ -1410,7 +1411,7 @@
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name")).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
verify(getServices().ibackupManager, times(1)).setBackupServiceActive(
eq(UserHandle.USER_SYSTEM), eq(false));
@@ -1451,7 +1452,7 @@
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name")).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
// Verify internal calls.
verify(getServices().iactivityManager, times(1)).updateDeviceOwner(
@@ -3026,7 +3027,7 @@
// Set a device owner on the system user. Check that the system user becomes affiliated.
setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
dpm.setActiveAdmin(admin1, /* replace =*/ false);
- assertThat(dpm.setDeviceOwner(admin1, "owner-name")).isTrue();
+ assertThat(dpm.setDeviceOwner(admin1, "owner-name", UserHandle.USER_SYSTEM)).isTrue();
assertThat(dpm.isAffiliatedUser()).isTrue();
assertThat(dpm.getAffiliationIds(admin1).isEmpty()).isTrue();
@@ -7754,6 +7755,12 @@
DpmMockContext.CALLER_SYSTEM_USER_UID, admin1.getPackageName(), MODE_DEFAULT);
}
+ @Test
+ public void testGetOrganizationNameForUser_calledByNonPrivilegedApp_throwsException() {
+ assertExpectException(SecurityException.class, "Calling identity is not authorized",
+ () -> dpm.getOrganizationNameForUser(UserHandle.USER_SYSTEM));
+ }
+
private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) {
final AppOpsManager.PackageOps vpnOp = new AppOpsManager.PackageOps(userVpnPackage,
userVpnUid, List.of(new AppOpsManager.OpEntry(
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 4564296..0dd5c61 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -20,6 +20,8 @@
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE;
@@ -1408,6 +1410,12 @@
public void testHbmVoting_forHdr() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+ final int hbmRefreshRate = 72;
+
+ // Specify limitation before starting DisplayModeDirector to avoid waiting on property
+ // propagation
+ mInjector.getDeviceConfig().setRefreshRateInHbmHdr(hbmRefreshRate);
+
director.start(createMockSensorManager());
ArgumentCaptor<DisplayListener> captor =
@@ -1432,7 +1440,7 @@
new BrightnessInfo(0.45f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
- assertVoteForRefreshRate(vote, 60.f);
+ assertVoteForRefreshRate(vote, hbmRefreshRate);
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
@@ -1443,6 +1451,44 @@
}
@Test
+ public void testHbmObserverGetsUpdatedRefreshRateInHbmSunlight() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
+
+ final int initialRefreshRate = 60;
+ mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(initialRefreshRate);
+ director.start(createMockSensorManager());
+ assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight())
+ .isEqualTo(initialRefreshRate);
+
+ final int updatedRefreshRate = 90;
+ mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(updatedRefreshRate);
+ // Need to wait for the property change to propagate to the main thread.
+ waitForIdleSync();
+ assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight())
+ .isEqualTo(updatedRefreshRate);
+ }
+
+ @Test
+ public void testHbmObserverGetsUpdatedRefreshRateInHbmHdr() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, /* baseModeId= */ 0);
+
+ final int initialRefreshRate = 60;
+ mInjector.getDeviceConfig().setRefreshRateInHbmHdr(initialRefreshRate);
+ director.start(createMockSensorManager());
+ assertThat(director.getHbmObserver().getRefreshRateInHbmHdr())
+ .isEqualTo(initialRefreshRate);
+
+ final int updatedRefreshRate = 90;
+ mInjector.getDeviceConfig().setRefreshRateInHbmHdr(updatedRefreshRate);
+ // Need to wait for the property change to propagate to the main thread.
+ waitForIdleSync();
+ assertThat(director.getHbmObserver().getRefreshRateInHbmHdr())
+ .isEqualTo(updatedRefreshRate);
+ }
+
+ @Test
public void testHbmVoting_forSunlight() {
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
@@ -1455,11 +1501,12 @@
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
DisplayListener listener = captor.getValue();
+ final int initialRefreshRate = 60;
// Specify Limitation
when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID)).thenReturn(
List.of(new RefreshRateLimitation(
DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE,
- 60.f, 60.f)));
+ initialRefreshRate, initialRefreshRate)));
// Verify that there is no HBM vote initially
Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
@@ -1470,7 +1517,39 @@
new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
listener.onDisplayChanged(DISPLAY_ID);
vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
- assertVoteForRefreshRate(vote, 60.f);
+ assertVoteForRefreshRate(vote, initialRefreshRate);
+
+ // Change refresh rate vote value through DeviceConfig, ensure it takes precedence
+ final int updatedRefreshRate = 90;
+ mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(updatedRefreshRate);
+ // Need to wait for the property change to propagate to the main thread.
+ waitForIdleSync();
+ assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight())
+ .isEqualTo(updatedRefreshRate);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertVoteForRefreshRate(vote, updatedRefreshRate);
+
+ // Turn off HBM
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(0.43f, 0.1f, 0.8f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Turn HBM on again and ensure the updated vote value stuck
+ when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
+ new BrightnessInfo(1.0f, 0.0f, 1.0f, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT));
+ listener.onDisplayChanged(DISPLAY_ID);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertVoteForRefreshRate(vote, updatedRefreshRate);
+
+ // Reset DeviceConfig refresh rate, ensure vote falls back to the initial value
+ mInjector.getDeviceConfig().setRefreshRateInHbmSunlight(0);
+ // Need to wait for the property change to propagate to the main thread.
+ waitForIdleSync();
+ assertThat(director.getHbmObserver().getRefreshRateInHbmSunlight()).isEqualTo(0);
+ vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertVoteForRefreshRate(vote, initialRefreshRate);
// Turn off HBM
when(mInjector.getBrightnessInfo(DISPLAY_ID)).thenReturn(
@@ -1518,6 +1597,63 @@
assertNull(vote);
}
+ private void setHbmAndAssertRefreshRate(
+ DisplayModeDirector director, DisplayListener listener, int mode, float rr) {
+ when(mInjector.getBrightnessInfo(DISPLAY_ID))
+ .thenReturn(new BrightnessInfo(1.0f, 0.0f, 1.0f, mode));
+ listener.onDisplayChanged(DISPLAY_ID);
+
+ final Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ if (Float.isNaN(rr)) {
+ assertNull(vote);
+ } else {
+ assertVoteForRefreshRate(vote, rr);
+ }
+ }
+
+ @Test
+ public void testHbmVoting_forSunlightAndHdr() {
+ DisplayModeDirector director =
+ createDirectorFromRefreshRateArray(new float[] {60.0f, 90.0f}, 0);
+
+ // Specify HDR limitation before starting DisplayModeDirector to avoid waiting on property
+ // propagation
+ final int hdrRr = 60;
+ mInjector.getDeviceConfig().setRefreshRateInHbmHdr(hdrRr);
+ director.start(createMockSensorManager());
+
+ ArgumentCaptor<DisplayListener> captor = ArgumentCaptor.forClass(DisplayListener.class);
+ verify(mInjector).registerDisplayListener(captor.capture(), any(Handler.class),
+ eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS
+ | DisplayManager.EVENT_FLAG_DISPLAY_REMOVED));
+ DisplayListener listener = captor.getValue();
+
+ // Specify Sunlight limitations
+ final float sunlightRr = 90.0f;
+ when(mDisplayManagerInternalMock.getRefreshRateLimitations(DISPLAY_ID))
+ .thenReturn(List.of(new RefreshRateLimitation(
+ DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, sunlightRr,
+ sunlightRr)));
+
+ // Verify that there is no HBM vote initially
+ Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_HIGH_BRIGHTNESS_MODE);
+ assertNull(vote);
+
+ // Verify all state transitions
+ setHbmAndAssertRefreshRate(
+ director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, sunlightRr);
+ setHbmAndAssertRefreshRate(
+ director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, hdrRr);
+ setHbmAndAssertRefreshRate(
+ director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, Float.NaN);
+ setHbmAndAssertRefreshRate(
+ director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR, hdrRr);
+ setHbmAndAssertRefreshRate(
+ director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT, sunlightRr);
+ setHbmAndAssertRefreshRate(
+ director, listener, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF, Float.NaN);
+ }
+
@Test
public void testHbmVoting_RemovedDisplay() {
DisplayModeDirector director =
@@ -1622,6 +1758,16 @@
String.valueOf(fps));
}
+ void setRefreshRateInHbmSunlight(int fps) {
+ putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ KEY_REFRESH_RATE_IN_HBM_SUNLIGHT, String.valueOf(fps));
+ }
+
+ void setRefreshRateInHbmHdr(int fps) {
+ putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ KEY_REFRESH_RATE_IN_HBM_HDR, String.valueOf(fps));
+ }
+
void setLowDisplayBrightnessThresholds(int[] brightnessThresholds) {
String thresholds = toPropertyValue(brightnessThresholds);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
index f3070b6..8137d12 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromPlaybackTest.java
@@ -65,31 +65,13 @@
private static final byte[] POWER_ON = new byte[] { POWER_STATUS_ON };
private static final byte[] POWER_STANDBY = new byte[] { POWER_STATUS_STANDBY };
private static final byte[] POWER_TRANSIENT_TO_ON = new byte[] { POWER_STATUS_TRANSIENT_TO_ON };
- private static final HdmiCecMessage REPORT_POWER_STATUS_ON = new HdmiCecMessage(
- ADDR_PLAYBACK_2, ADDR_PLAYBACK_1, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
- private static final HdmiCecMessage REPORT_POWER_STATUS_STANDBY = new HdmiCecMessage(
- ADDR_PLAYBACK_2, ADDR_PLAYBACK_1, Constants.MESSAGE_REPORT_POWER_STATUS, POWER_STANDBY);
- private static final HdmiCecMessage REPORT_POWER_STATUS_TRANSIENT_TO_ON = new HdmiCecMessage(
- ADDR_PLAYBACK_2, ADDR_PLAYBACK_1, Constants.MESSAGE_REPORT_POWER_STATUS,
- POWER_TRANSIENT_TO_ON);
- private static final HdmiCecMessage SET_STREAM_PATH = HdmiCecMessageBuilder.buildSetStreamPath(
- ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_2);
- private static final HdmiCecMessage ROUTING_CHANGE = HdmiCecMessageBuilder.buildRoutingChange(
- ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_2);
- private static final HdmiCecMessage ACTIVE_SOURCE = HdmiCecMessageBuilder.buildActiveSource(
- ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2);
- private static final HdmiDeviceInfo INFO_PLAYBACK_1 = new HdmiDeviceInfo(
- ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_1, PORT_1, HdmiDeviceInfo.DEVICE_PLAYBACK,
- 0x1234, "Playback 1",
- HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
- private static final HdmiDeviceInfo INFO_PLAYBACK_2 = new HdmiDeviceInfo(
- ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2, PORT_2, HdmiDeviceInfo.DEVICE_PLAYBACK,
- 0x1234, "Playback 2",
- HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
- private static final HdmiDeviceInfo INFO_PLAYBACK_3 = new HdmiDeviceInfo(
- ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3, PORT_3, HdmiDeviceInfo.DEVICE_PLAYBACK,
- 0x1234, "Playback 3",
- HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+
+ private HdmiCecMessage mReportPowerStatusOn;
+ private HdmiCecMessage mReportPowerStatusStandby;
+ private HdmiCecMessage mReportPowerStatusTransientToOn;
+ private HdmiCecMessage mSetStreamPath;
+ private HdmiCecMessage mRoutingChange;
+ private HdmiCecMessage mActiveSource;
private HdmiCecController mHdmiCecController;
private HdmiCecLocalDevicePlayback mHdmiCecLocalDevicePlayback;
@@ -103,6 +85,10 @@
private TestLooper mTestLooper = new TestLooper();
private ArrayList<HdmiCecLocalDevice> mLocalDevices = new ArrayList<>();
+ private int mPlaybackLogicalAddress1;
+ private int mPlaybackLogicalAddress2;
+ private int mPlaybackLogicalAddress3;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -157,9 +143,54 @@
mTestLooper.dispatchAll();
mNativeWrapper.clearResultMessages();
- mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_1);
- mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_2);
- mHdmiControlService.getHdmiCecNetwork().addCecDevice(INFO_PLAYBACK_3);
+ // The addresses depend on local device's LA.
+ // This help the tests to pass with every local device LA.
+ synchronized (mHdmiCecLocalDevicePlayback.mLock) {
+ mPlaybackLogicalAddress1 =
+ mHdmiCecLocalDevicePlayback.getDeviceInfo().getLogicalAddress();
+ }
+ mPlaybackLogicalAddress2 = mPlaybackLogicalAddress1 == ADDR_PLAYBACK_2
+ ? ADDR_PLAYBACK_1 : ADDR_PLAYBACK_2;
+ mPlaybackLogicalAddress3 = mPlaybackLogicalAddress1 == ADDR_PLAYBACK_3
+ ? ADDR_PLAYBACK_1 : ADDR_PLAYBACK_3;
+
+ mReportPowerStatusOn = new HdmiCecMessage(
+ mPlaybackLogicalAddress2, mPlaybackLogicalAddress1,
+ Constants.MESSAGE_REPORT_POWER_STATUS, POWER_ON);
+ mReportPowerStatusStandby = new HdmiCecMessage(
+ mPlaybackLogicalAddress2, mPlaybackLogicalAddress1,
+ Constants.MESSAGE_REPORT_POWER_STATUS, POWER_STANDBY);
+ mReportPowerStatusTransientToOn = new HdmiCecMessage(
+ mPlaybackLogicalAddress2, mPlaybackLogicalAddress1,
+ Constants.MESSAGE_REPORT_POWER_STATUS, POWER_TRANSIENT_TO_ON);
+ mSetStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(
+ mPlaybackLogicalAddress1, PHYSICAL_ADDRESS_PLAYBACK_2);
+ mRoutingChange = HdmiCecMessageBuilder.buildRoutingChange(
+ mPlaybackLogicalAddress1, PHYSICAL_ADDRESS_PLAYBACK_3,
+ PHYSICAL_ADDRESS_PLAYBACK_2);
+ mActiveSource = HdmiCecMessageBuilder.buildActiveSource(
+ mPlaybackLogicalAddress2, PHYSICAL_ADDRESS_PLAYBACK_2);
+
+ HdmiDeviceInfo infoPlayback1 = new HdmiDeviceInfo(
+ mPlaybackLogicalAddress1, PHYSICAL_ADDRESS_PLAYBACK_1, PORT_1,
+ HdmiDeviceInfo.DEVICE_PLAYBACK,
+ 0x1234, "Playback 1",
+ HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+ HdmiDeviceInfo infoPlayback2 = new HdmiDeviceInfo(
+ mPlaybackLogicalAddress2, PHYSICAL_ADDRESS_PLAYBACK_2, PORT_2,
+ HdmiDeviceInfo.DEVICE_PLAYBACK,
+ 0x1234, "Playback 2",
+ HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+ HdmiDeviceInfo infoPlayback3 = new HdmiDeviceInfo(
+ mPlaybackLogicalAddress3, PHYSICAL_ADDRESS_PLAYBACK_3, PORT_3,
+ HdmiDeviceInfo.DEVICE_PLAYBACK,
+ 0x1234, "Playback 3",
+ HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback1);
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback2);
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback3);
+
}
private static class TestActionTimer implements ActionTimer {
@@ -198,7 +229,7 @@
TestCallback callback,
boolean isCec20) {
HdmiDeviceInfo hdmiDeviceInfo =
- mHdmiControlService.getHdmiCecNetwork().getCecDeviceInfo(ADDR_PLAYBACK_2);
+ mHdmiControlService.getHdmiCecNetwork().getCecDeviceInfo(mPlaybackLogicalAddress2);
DeviceSelectActionFromPlayback action = new DeviceSelectActionFromPlayback(
mHdmiCecLocalDevicePlayback,
hdmiDeviceInfo, callback, isCec20);
@@ -214,23 +245,23 @@
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/false);
- mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ mHdmiControlService.setActiveSource(mPlaybackLogicalAddress3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
action.start();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
- action.processCommand(REPORT_POWER_STATUS_ON);
+ action.processCommand(mReportPowerStatusOn);
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
- action.processCommand(ACTIVE_SOURCE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
+ action.processCommand(mActiveSource);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_DeviceInStandbyStatus_Cec14b() {
- mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ mHdmiControlService.setActiveSource(mPlaybackLogicalAddress3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
@@ -238,25 +269,25 @@
callback, /*isCec20=*/false);
action.start();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
- action.processCommand(REPORT_POWER_STATUS_STANDBY);
+ action.processCommand(mReportPowerStatusStandby);
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
- action.processCommand(REPORT_POWER_STATUS_ON);
+ action.processCommand(mReportPowerStatusOn);
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
- action.processCommand(ACTIVE_SOURCE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
+ action.processCommand(mActiveSource);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_DeviceInStandbyStatusWithSomeTimeouts_Cec14b() {
- mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ mHdmiControlService.setActiveSource(mPlaybackLogicalAddress3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
@@ -264,27 +295,27 @@
callback, /*isCec20=*/false);
action.start();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
- action.processCommand(REPORT_POWER_STATUS_STANDBY);
+ action.processCommand(mReportPowerStatusStandby);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
- action.processCommand(REPORT_POWER_STATUS_TRANSIENT_TO_ON);
+ action.processCommand(mReportPowerStatusTransientToOn);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
- action.processCommand(REPORT_POWER_STATUS_ON);
+ action.processCommand(mReportPowerStatusOn);
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
- action.processCommand(ACTIVE_SOURCE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
+ action.processCommand(mActiveSource);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_DeviceInStandbyAfterTimeoutForReportPowerStatus_Cec14b() {
- mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ mHdmiControlService.setActiveSource(mPlaybackLogicalAddress3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
@@ -292,118 +323,118 @@
callback, /*isCec20=*/false);
action.start();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
- action.processCommand(REPORT_POWER_STATUS_STANDBY);
+ action.processCommand(mReportPowerStatusStandby);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
- action.processCommand(REPORT_POWER_STATUS_TRANSIENT_TO_ON);
+ action.processCommand(mReportPowerStatusTransientToOn);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
action.handleTimerEvent(STATE_WAIT_FOR_REPORT_POWER_STATUS);
// Give up getting power status, and just send <Routing Change>
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
- action.processCommand(ACTIVE_SOURCE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
+ action.processCommand(mActiveSource);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
- public void testDeviceSelect_ReachSetStreamPath_Cec14b() {
+ public void testDeviceSelect_ReachmSetStreamPath_Cec14b() {
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/false);
- mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ mHdmiControlService.setActiveSource(mPlaybackLogicalAddress3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
action.start();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
- action.processCommand(REPORT_POWER_STATUS_ON);
+ action.processCommand(mReportPowerStatusOn);
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(
STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE);
action.handleTimerEvent(STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE);
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
- action.processCommand(ACTIVE_SOURCE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mSetStreamPath);
+ action.processCommand(mActiveSource);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
- public void testDeviceSelect_ReachSetStreamPathDeviceInPowerOnStatus_Cec20() {
- mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_2,
+ public void testDeviceSelect_ReachmSetStreamPathDeviceInPowerOnStatus_Cec20() {
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(mPlaybackLogicalAddress2,
HdmiControlManager.POWER_STATUS_ON);
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/true);
- mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ mHdmiControlService.setActiveSource(mPlaybackLogicalAddress3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
action.start();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(
STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE);
action.handleTimerEvent(STATE_WAIT_FOR_ACTIVE_SOURCE_MESSAGE_AFTER_ROUTING_CHANGE);
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
- action.processCommand(ACTIVE_SOURCE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mSetStreamPath);
+ action.processCommand(mActiveSource);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_DeviceInPowerOnStatus_Cec20() {
- mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_2,
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(mPlaybackLogicalAddress2,
HdmiControlManager.POWER_STATUS_ON);
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/true);
- mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ mHdmiControlService.setActiveSource(mPlaybackLogicalAddress3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
action.start();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
- action.processCommand(ACTIVE_SOURCE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
+ action.processCommand(mActiveSource);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_DeviceInPowerUnknownStatus_Cec20() {
- mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_2,
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(mPlaybackLogicalAddress2,
HdmiControlManager.POWER_STATUS_UNKNOWN);
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
DeviceSelectActionFromPlayback action = createDeviceSelectActionFromPlayback(actionTimer,
callback, /*isCec20=*/true);
- mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ mHdmiControlService.setActiveSource(mPlaybackLogicalAddress3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
action.start();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
- action.processCommand(REPORT_POWER_STATUS_ON);
+ action.processCommand(mReportPowerStatusOn);
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
- action.processCommand(ACTIVE_SOURCE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
+ action.processCommand(mActiveSource);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
@Test
public void testDeviceSelect_DeviceInStandbyStatus_Cec20() {
- mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(ADDR_PLAYBACK_2,
+ mHdmiControlService.getHdmiCecNetwork().updateDevicePowerStatus(mPlaybackLogicalAddress2,
HdmiControlManager.POWER_STATUS_STANDBY);
- mHdmiControlService.setActiveSource(ADDR_PLAYBACK_3, PHYSICAL_ADDRESS_PLAYBACK_3,
+ mHdmiControlService.setActiveSource(mPlaybackLogicalAddress3, PHYSICAL_ADDRESS_PLAYBACK_3,
"testDeviceSelectFromPlayback");
TestActionTimer actionTimer = new TestActionTimer();
TestCallback callback = new TestCallback();
@@ -411,18 +442,18 @@
callback, /*isCec20=*/true);
action.start();
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
- action.processCommand(REPORT_POWER_STATUS_STANDBY);
+ action.processCommand(mReportPowerStatusStandby);
mTestLooper.dispatchAll();
mNativeWrapper.clearResultMessages();
assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_DEVICE_POWER_ON);
action.handleTimerEvent(STATE_WAIT_FOR_DEVICE_POWER_ON);
- action.processCommand(REPORT_POWER_STATUS_ON);
+ action.processCommand(mReportPowerStatusOn);
mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(ROUTING_CHANGE);
- action.processCommand(ACTIVE_SOURCE);
+ assertThat(mNativeWrapper.getResultMessages()).contains(mRoutingChange);
+ action.processCommand(mActiveSource);
assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 8a84c6f..055459c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -29,6 +29,7 @@
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiControlCallback;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
import android.os.RemoteException;
import android.os.test.TestLooper;
@@ -54,6 +55,8 @@
/** Tests for {@link HdmiCecLocalDevicePlayback} class. */
public class HdmiCecLocalDevicePlaybackTest {
private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1;
+ private static final int HOTPLUG_INTERVAL =
+ HotplugDetectionAction.POLLING_INTERVAL_MS_FOR_PLAYBACK;
private static final int PORT_1 = 1;
private static final HdmiDeviceInfo INFO_TV = new HdmiDeviceInfo(
@@ -1646,6 +1649,60 @@
}
@Test
+ public void onAddressAllocated_invokesDeviceDiscovery() {
+ mNativeWrapper.setPollAddressResponse(Constants.ADDR_PLAYBACK_2, SendMessageResult.SUCCESS);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+
+ mTestLooper.dispatchAll();
+
+ // Check for <Give Physical Address> being sent to available device (ADDR_PLAYBACK_2).
+ // This message is sent as part of the DeviceDiscoveryAction to available devices.
+ HdmiCecMessage givePhysicalAddress = HdmiCecMessageBuilder.buildGivePhysicalAddress(
+ Constants.ADDR_PLAYBACK_1,
+ Constants.ADDR_PLAYBACK_2);
+ assertThat(mNativeWrapper.getResultMessages()).contains(givePhysicalAddress);
+ }
+
+ @Test
+ public void hotplugDetectionAction_addDevice() {
+ int otherPlaybackLogicalAddress = mPlaybackLogicalAddress == Constants.ADDR_PLAYBACK_2
+ ? Constants.ADDR_PLAYBACK_1 : Constants.ADDR_PLAYBACK_2;
+ mNativeWrapper.setPollAddressResponse(otherPlaybackLogicalAddress,
+ SendMessageResult.NACK);
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mTestLooper.dispatchAll();
+
+ mNativeWrapper.setPollAddressResponse(otherPlaybackLogicalAddress,
+ SendMessageResult.SUCCESS);
+ mTestLooper.moveTimeForward(HOTPLUG_INTERVAL);
+ mTestLooper.dispatchAll();
+
+ // Check for <Give Physical Address> being sent to the newly discovered device.
+ // This message is sent as part of the HotplugDetectionAction to available devices.
+ HdmiCecMessage givePhysicalAddress = HdmiCecMessageBuilder.buildGivePhysicalAddress(
+ mPlaybackLogicalAddress, otherPlaybackLogicalAddress);
+ assertThat(mNativeWrapper.getResultMessages()).contains(givePhysicalAddress);
+ }
+
+ @Test
+ public void hotplugDetectionAction_removeDevice() {
+ mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+ mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
+ HdmiDeviceInfo infoPlayback = new HdmiDeviceInfo(
+ Constants.ADDR_PLAYBACK_2, 0x1234, PORT_1,
+ HdmiDeviceInfo.DEVICE_PLAYBACK, 0x1234, "Playback 2",
+ HdmiControlManager.POWER_STATUS_ON, HdmiControlManager.HDMI_CEC_VERSION_1_4_B);
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback);
+ // This logical address (ADDR_PLAYBACK_2) won't acknowledge the poll message sent by the
+ // HotplugDetectionAction so it shall be removed.
+ mNativeWrapper.setPollAddressResponse(Constants.ADDR_PLAYBACK_2, SendMessageResult.NACK);
+ mTestLooper.moveTimeForward(HOTPLUG_INTERVAL);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false)).isEmpty();
+ }
+
+ @Test
public void getActiveSource_noActiveSource() {
mHdmiControlService.setActiveSource(Constants.ADDR_UNREGISTERED,
Constants.INVALID_PHYSICAL_ADDRESS, "HdmiControlServiceTest");
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 7acf946..372fd60 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -52,6 +52,7 @@
import java.util.ArrayList;
import java.util.Collections;
+import java.util.concurrent.TimeUnit;
@SmallTest
@RunWith(JUnit4.class)
@@ -522,4 +523,31 @@
verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt());
}
+
+ /**
+ * Tests that receiving a message from a device does not prevent it from being discovered
+ * by HotplugDetectionAction.
+ */
+ @Test
+ public void hotplugDetectionAction_discoversDeviceAfterMessageReceived() {
+ // Playback 1 sends a message before ACKing a poll
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.NACK);
+ HdmiCecMessage hdmiCecMessage = HdmiCecMessageBuilder.buildActiveSource(
+ ADDR_PLAYBACK_1, ADDR_TV);
+ mNativeWrapper.onCecMessage(hdmiCecMessage);
+ mTestLooper.dispatchAll();
+
+ // Playback 1 begins ACKing polls, allowing detection by HotplugDetectionAction
+ mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+ for (int pollCount = 0; pollCount < HotplugDetectionAction.TIMEOUT_COUNT; pollCount++) {
+ mTestLooper.moveTimeForward(
+ TimeUnit.SECONDS.toMillis(HotplugDetectionAction.POLLING_INTERVAL_MS_FOR_TV));
+ mTestLooper.dispatchAll();
+ }
+
+ // Device sends <Give Physical Address> to Playback 1 after detecting it
+ HdmiCecMessage givePhysicalAddress = HdmiCecMessageBuilder.buildGivePhysicalAddress(
+ ADDR_TV, ADDR_PLAYBACK_1);
+ assertThat(mNativeWrapper.getResultMessages()).contains(givePhysicalAddress);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
index a12aa29..2b49095 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PowerStatusMonitorActionTest.java
@@ -46,7 +46,7 @@
import java.util.Collections;
import java.util.concurrent.TimeUnit;
-/** Tests for {@link ActiveSourceAction} */
+/** Tests for {@link PowerStatusMonitorAction} */
@SmallTest
@RunWith(JUnit4.class)
public class PowerStatusMonitorActionTest {
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 9f12438..76f233c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -914,7 +914,8 @@
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
- DUMMY_CALLING_APPID, withInstallSource(target.name, null, null, null, false));
+ DUMMY_CALLING_APPID,
+ withInstallSource(target.getPackageName(), null, null, null, false));
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
SYSTEM_USER));
@@ -931,7 +932,8 @@
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
DUMMY_TARGET_APPID);
PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
- DUMMY_CALLING_APPID, withInstallSource(target.name, null, null, null, true));
+ DUMMY_CALLING_APPID,
+ withInstallSource(target.getPackageName(), null, null, null, true));
assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
SYSTEM_USER));
@@ -953,7 +955,8 @@
DUMMY_TARGET_APPID);
watcher.verifyChangeReported("add package");
PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
- DUMMY_CALLING_APPID, withInstallSource(null, target.name, null, null, false));
+ DUMMY_CALLING_APPID, withInstallSource(null, target.getPackageName(), null, null,
+ false));
watcher.verifyChangeReported("add package");
assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
@@ -977,7 +980,8 @@
DUMMY_TARGET_APPID);
watcher.verifyChangeReported("add package");
PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
- DUMMY_CALLING_APPID, withInstallSource(null, null, target.name, null, false));
+ DUMMY_CALLING_APPID, withInstallSource(null, null, target.getPackageName(), null,
+ false));
watcher.verifyChangeReported("add package");
assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
@@ -1233,7 +1237,7 @@
mExisting.put(newPkg.getPackageName(), setting);
if (sharedUserSetting != null) {
sharedUserSetting.addPackage(setting);
- setting.sharedUser = sharedUserSetting;
+ setting.setSharedUser(sharedUserSetting);
}
filter.addPackage(setting);
return setting;
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 15f57e3..9631863 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -116,7 +116,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
@@ -132,7 +132,7 @@
ArraySet<Long> mapping = ksMapping.get(1);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(1)));
- assertEquals(1, ps.keySetData.getProperSigningKeySet());
+ assertEquals(1, ps.getKeySetData().getProperSigningKeySet());
}
/*
@@ -143,7 +143,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
@@ -162,7 +162,7 @@
ArraySet<Long> mapping = ksMapping.get(1);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(1)));
- assertEquals(1, ps.keySetData.getProperSigningKeySet());
+ assertEquals(1, ps.getKeySetData().getProperSigningKeySet());
}
/*
@@ -173,7 +173,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
@@ -197,7 +197,7 @@
ArraySet<Long> mapping = ksMapping.get(2);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(2)));
- assertEquals(2, ps.keySetData.getProperSigningKeySet());
+ assertEquals(2, ps.getKeySetData().getProperSigningKeySet());
}
/*
@@ -208,9 +208,9 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps1 = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps1.name, ps1);
+ mPackagesMap.put(ps1.getPackageName(), ps1);
PackageSetting ps2 = generateFakePackageSetting("packageB");
- mPackagesMap.put(ps2.name, ps2);
+ mPackagesMap.put(ps2.getPackageName(), ps2);
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
@@ -239,8 +239,8 @@
mapping = ksMapping.get(2);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(2)));
- assertEquals(2, ps1.keySetData.getProperSigningKeySet());
- assertEquals(1, ps2.keySetData.getProperSigningKeySet());
+ assertEquals(2, ps1.getKeySetData().getProperSigningKeySet());
+ assertEquals(1, ps2.getKeySetData().getProperSigningKeySet());
}
/*
@@ -251,9 +251,9 @@
/* create PackageSettings and add to Settings mPackages */
PackageSetting ps1 = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps1.name, ps1);
+ mPackagesMap.put(ps1.getPackageName(), ps1);
PackageSetting ps2 = generateFakePackageSetting("packageB");
- mPackagesMap.put(ps2.name, ps2);
+ mPackagesMap.put(ps2.getPackageName(), ps2);
/* collect signing key and add */
ArraySet<PublicKey> signingKeys1 = new ArraySet<PublicKey>();
@@ -276,7 +276,7 @@
ArraySet<Long> mapping = ksMapping.get(1);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(1)));
- assertEquals(1, ps1.keySetData.getProperSigningKeySet());
+ assertEquals(1, ps1.getKeySetData().getProperSigningKeySet());
/* verify second */
assertEquals(1, KeySetUtils.getKeySetRefCount(mKsms, 2));
@@ -285,7 +285,7 @@
mapping = ksMapping.get(2);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(2)));
- assertEquals(2, ps2.keySetData.getProperSigningKeySet());
+ assertEquals(2, ps2.getKeySetData().getProperSigningKeySet());
}
/*
@@ -296,9 +296,9 @@
/* create PackageSettings and add to Settings mPackages */
PackageSetting ps1 = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps1.name, ps1);
+ mPackagesMap.put(ps1.getPackageName(), ps1);
PackageSetting ps2 = generateFakePackageSetting("packageB");
- mPackagesMap.put(ps2.name, ps2);
+ mPackagesMap.put(ps2.getPackageName(), ps2);
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
@@ -317,8 +317,8 @@
ArraySet<Long> mapping = ksMapping.get(1);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(1)));
- assertEquals(1, ps1.keySetData.getProperSigningKeySet());
- assertEquals(1, ps2.keySetData.getProperSigningKeySet());
+ assertEquals(1, ps1.getKeySetData().getProperSigningKeySet());
+ assertEquals(1, ps2.getKeySetData().getProperSigningKeySet());
}
/*
@@ -329,9 +329,9 @@
/* create PackageSettings and add to Settings mPackages */
PackageSetting ps1 = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps1.name, ps1);
+ mPackagesMap.put(ps1.getPackageName(), ps1);
PackageSetting ps2 = generateFakePackageSetting("packageB");
- mPackagesMap.put(ps2.name, ps2);
+ mPackagesMap.put(ps2.getPackageName(), ps2);
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
@@ -359,8 +359,8 @@
assertEquals(2, mapping.size());
assertTrue(mapping.contains(new Long(1)));
assertTrue(mapping.contains(new Long(2)));
- assertEquals(1, ps1.keySetData.getProperSigningKeySet());
- assertEquals(2, ps2.keySetData.getProperSigningKeySet());
+ assertEquals(1, ps1.getKeySetData().getProperSigningKeySet());
+ assertEquals(2, ps2.getKeySetData().getProperSigningKeySet());
}
/*
@@ -372,7 +372,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
@@ -401,7 +401,7 @@
assertEquals(2, mapping.size());
assertTrue(mapping.contains(new Long(2)));
assertTrue(mapping.contains(new Long(3)));
- assertEquals(2, ps.keySetData.getProperSigningKeySet());
+ assertEquals(2, ps.getKeySetData().getProperSigningKeySet());
}
/* add a defined keyset make sure it shows up */
@@ -409,7 +409,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
@@ -427,8 +427,8 @@
ArraySet<Long> mapping = ksMapping.get(1);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(1)));
- assertNotNull(ps.keySetData.getAliases().get("aliasA"));
- assertEquals(new Long(1), ps.keySetData.getAliases().get("aliasA"));
+ assertNotNull(ps.getKeySetData().getAliases().get("aliasA"));
+ assertEquals(new Long(1), ps.getKeySetData().getAliases().get("aliasA"));
}
/* add 2 defined keysets which refer to same keyset and make sure ref-ct is 2 */
@@ -436,7 +436,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
@@ -455,10 +455,10 @@
ArraySet<Long> mapping = ksMapping.get(1);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(1)));
- assertNotNull(ps.keySetData.getAliases().get("aliasA"));
- assertEquals(new Long(1), ps.keySetData.getAliases().get("aliasA"));
- assertNotNull(ps.keySetData.getAliases().get("aliasA2"));
- assertEquals(new Long(1), ps.keySetData.getAliases().get("aliasA2"));
+ assertNotNull(ps.getKeySetData().getAliases().get("aliasA"));
+ assertEquals(new Long(1), ps.getKeySetData().getAliases().get("aliasA"));
+ assertNotNull(ps.getKeySetData().getAliases().get("aliasA2"));
+ assertEquals(new Long(1), ps.getKeySetData().getAliases().get("aliasA2"));
}
/* upgrd defined keyset ortho (make sure previous is removed for pkg) */
@@ -466,7 +466,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
@@ -494,9 +494,9 @@
ArraySet<Long> mapping = ksMapping.get(2);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(2)));
- assertNull(ps.keySetData.getAliases().get("aliasA"));
- assertNotNull(ps.keySetData.getAliases().get("aliasB"));
- assertEquals(new Long(2), ps.keySetData.getAliases().get("aliasB"));
+ assertNull(ps.getKeySetData().getAliases().get("aliasA"));
+ assertNotNull(ps.getKeySetData().getAliases().get("aliasB"));
+ assertEquals(new Long(2), ps.getKeySetData().getAliases().get("aliasB"));
}
/* upgrd defined keyset ortho but reuse alias (make sure old is removed and
@@ -506,7 +506,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
@@ -533,8 +533,8 @@
ArraySet<Long> mapping = ksMapping.get(2);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(2)));
- assertNotNull(ps.keySetData.getAliases().get("aliasA"));
- assertEquals(new Long(2), ps.keySetData.getAliases().get("aliasA"));
+ assertNotNull(ps.getKeySetData().getAliases().get("aliasA"));
+ assertEquals(new Long(2), ps.getKeySetData().getAliases().get("aliasA"));
}
/* Start with defined ks of (A, B) and upgrade to (B, C). Make sure B is
@@ -543,7 +543,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect keys A and B and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
@@ -573,7 +573,7 @@
ArraySet<Long> mapping = ksMapping.get(3);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(3)));
- assertEquals(new Long(3), ps.keySetData.getAliases().get("aliasC"));
+ assertEquals(new Long(3), ps.getKeySetData().getAliases().get("aliasC"));
/* either keyset w/keyA or w/keyB was added first, address both cases */
if (1 == KeySetUtils.getKeySetRefCount(mKsms, 1)) {
@@ -586,7 +586,7 @@
mapping = ksMapping.get(1);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(1)));
- assertEquals(new Long(1), ps.keySetData.getAliases().get("aliasB"));
+ assertEquals(new Long(1), ps.getKeySetData().getAliases().get("aliasB"));
} else {
/* keyA was added first and keyB has id 2 */
@@ -598,9 +598,9 @@
mapping = ksMapping.get(2);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(2)));
- assertEquals(new Long(2), ps.keySetData.getAliases().get("aliasB"));
+ assertEquals(new Long(2), ps.getKeySetData().getAliases().get("aliasB"));
}
- assertNull(ps.keySetData.getAliases().get("aliasA"));
+ assertNull(ps.getKeySetData().getAliases().get("aliasA"));
}
/* add defined keyset, remove it, add again and make sure diff id. */
@@ -608,7 +608,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
@@ -643,7 +643,7 @@
ArraySet<Long> mapping = ksMapping.get(3);
assertEquals(1, mapping.size());
assertTrue(mapping.contains(new Long(3)));
- assertEquals(new Long(3), ps.keySetData.getAliases().get("aliasA"));
+ assertEquals(new Long(3), ps.getKeySetData().getAliases().get("aliasA"));
}
/* add upgrade keyset for existing defined keyset and check that it is recorded */
@@ -651,7 +651,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect key and add, and denote as an upgrade keyset */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
@@ -664,8 +664,8 @@
upgradeKS.add("aliasA");
mKsms.addUpgradeKeySetsToPackageLPw(ps, upgradeKS);
- assertEquals(1, ps.keySetData.getUpgradeKeySets().length);
- assertEquals(1, ps.keySetData.getUpgradeKeySets()[0]);
+ assertEquals(1, ps.getKeySetData().getUpgradeKeySets().length);
+ assertEquals(1, ps.getKeySetData().getUpgradeKeySets()[0]);
}
/* add upgrade keyset for non-existing defined and check that it compains */
@@ -673,7 +673,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect key and add and try to specify bogus upgrade keyset */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
@@ -700,7 +700,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
@@ -719,7 +719,7 @@
definedKS.remove("aliasA");
definedKS.put("aliasB", keys);
mKsms.addDefinedKeySetsToPackageLPw(ps, definedKS);
- assertNull(ps.keySetData.getUpgradeKeySets());
+ assertNull(ps.getKeySetData().getUpgradeKeySets());
}
/* remove package and validate that keyset and public keys are removed */
@@ -727,7 +727,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect signing key and add */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
@@ -736,12 +736,13 @@
mKsms.addSigningKeySetToPackageLPw(ps, signingKeys);
/* remove its references */
- mKsms.removeAppKeySetDataLPw(ps.name);
+ mKsms.removeAppKeySetDataLPw(ps.getPackageName());
assertEquals(0, KeySetUtils.getKeySetRefCount(mKsms, 1));
assertEquals(0, KeySetUtils.getPubKeyRefCount(mKsms, 1));
LongSparseArray<ArraySet<Long>> ksMapping = KeySetUtils.getKeySetMapping(mKsms);
assertEquals(0, ksMapping.size());
- assertEquals(PackageKeySetData.KEYSET_UNASSIGNED, ps.keySetData.getProperSigningKeySet());
+ assertEquals(PackageKeySetData.KEYSET_UNASSIGNED,
+ ps.getKeySetData().getProperSigningKeySet());
}
/* remove package and validate that keysets remain if defined elsewhere but
@@ -750,9 +751,9 @@
/* create PackageSettings and add to Settings mPackages */
PackageSetting ps1 = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps1.name, ps1);
+ mPackagesMap.put(ps1.getPackageName(), ps1);
PackageSetting ps2 = generateFakePackageSetting("packageB");
- mPackagesMap.put(ps2.name, ps2);
+ mPackagesMap.put(ps2.getPackageName(), ps2);
/* collect signing key and add for both packages */
ArraySet<PublicKey> signingKeys = new ArraySet<PublicKey>();
@@ -762,14 +763,15 @@
mKsms.addSigningKeySetToPackageLPw(ps2, signingKeys);
/* remove references from first package */
- mKsms.removeAppKeySetDataLPw(ps1.name);
+ mKsms.removeAppKeySetDataLPw(ps1.getPackageName());
assertEquals(1, KeySetUtils.getKeySetRefCount(mKsms, 1));
assertEquals(1, KeySetUtils.getPubKeyRefCount(mKsms, 1));
LongSparseArray<ArraySet<Long>> ksMapping = KeySetUtils.getKeySetMapping(mKsms);
assertEquals(1, ksMapping.size());
- assertEquals(PackageKeySetData.KEYSET_UNASSIGNED, ps1.keySetData.getProperSigningKeySet());
- assertEquals(1, ps2.keySetData.getProperSigningKeySet());
+ assertEquals(PackageKeySetData.KEYSET_UNASSIGNED,
+ ps1.getKeySetData().getProperSigningKeySet());
+ assertEquals(1, ps2.getKeySetData().getProperSigningKeySet());
}
/* remove package which used defined and upgrade keysets and ensure removed */
@@ -777,7 +779,7 @@
/* create PackageSetting and add to Settings mPackages */
PackageSetting ps = generateFakePackageSetting("packageA");
- mPackagesMap.put(ps.name, ps);
+ mPackagesMap.put(ps.getPackageName(), ps);
/* collect key and add */
ArrayMap<String, ArraySet<PublicKey>> definedKS = new ArrayMap<String, ArraySet<PublicKey>>();
@@ -795,14 +797,15 @@
ArraySet<String> upgradeKS = new ArraySet<String>();
upgradeKS.add("aliasA");
mKsms.addUpgradeKeySetsToPackageLPw(ps, upgradeKS);
- mKsms.removeAppKeySetDataLPw(ps.name);
+ mKsms.removeAppKeySetDataLPw(ps.getPackageName());
assertEquals(0, KeySetUtils.getKeySetRefCount(mKsms, 1));
assertEquals(0, KeySetUtils.getPubKeyRefCount(mKsms, 1));
LongSparseArray<ArraySet<Long>> ksMapping = KeySetUtils.getKeySetMapping(mKsms);
assertEquals(0, ksMapping.size());
- assertEquals(PackageKeySetData.KEYSET_UNASSIGNED, ps.keySetData.getProperSigningKeySet());
- assertEquals(0, ps.keySetData.getAliases().size());
- assertNull(ps.keySetData.getUpgradeKeySets());
+ assertEquals(PackageKeySetData.KEYSET_UNASSIGNED,
+ ps.getKeySetData().getProperSigningKeySet());
+ assertEquals(0, ps.getKeySetData().getAliases().size());
+ assertNull(ps.getKeySetData().getUpgradeKeySets());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 76741c4..33099bb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -426,16 +426,16 @@
public void testWriteReadUsesStaticLibraries() {
final Settings settingsUnderTest = makeSettings();
final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
- ps1.appId = Process.FIRST_APPLICATION_UID;
- ps1.pkg = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed())
- .setUid(ps1.appId)
+ ps1.setAppId(Process.FIRST_APPLICATION_UID);
+ ps1.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed())
+ .setUid(ps1.getAppId())
.setSystem(true)
- .hideAsFinal();
+ .hideAsFinal());
final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
- ps2.appId = Process.FIRST_APPLICATION_UID + 1;
- ps2.pkg = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_2).hideAsParsed())
- .setUid(ps2.appId)
- .hideAsFinal();
+ ps2.setAppId(Process.FIRST_APPLICATION_UID + 1);
+ ps2.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_2).hideAsParsed())
+ .setUid(ps2.getAppId())
+ .hideAsFinal());
ps1.usesStaticLibraries = new String[] { "com.example.shared.one" };
ps1.usesStaticLibrariesVersions = new long[] { 12 };
@@ -603,7 +603,7 @@
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
UUID.randomUUID());
- testPkgSetting01.copyFrom(origPkgSetting01);
+ testPkgSetting01.copyPackageSetting(origPkgSetting01);
verifySettingCopy(origPkgSetting01, testPkgSetting01);
}
@@ -631,8 +631,8 @@
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
UUID.randomUUID());
- assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
- assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
+ assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
+ assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
assertThat(testPkgSetting01.pkgFlags, is(0));
assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
final PackageUserState userState = testPkgSetting01.readUserState(0);
@@ -665,8 +665,8 @@
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
UUID.randomUUID());
- assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
- assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
+ assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
+ assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
final PackageUserState userState = testPkgSetting01.readUserState(0);
@@ -712,7 +712,7 @@
public void testCreateNewSetting01() {
final PackageSetting originalPkgSetting01 =
createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
- final PackageSignatures originalSignatures = originalPkgSetting01.signatures;
+ final PackageSignatures originalSignatures = originalPkgSetting01.getSignatures();
final PackageSetting testPkgSetting01 = Settings.createNewSetting(
REAL_PACKAGE_NAME,
originalPkgSetting01 /*originalPkg*/,
@@ -736,14 +736,14 @@
null /*mimeGroups*/,
UUID.randomUUID());
assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
- assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
+ assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
- assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
- assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
+ assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
+ assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
// signatures object must be different
- assertNotSame(testPkgSetting01.signatures, originalSignatures);
- assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE));
+ assertNotSame(testPkgSetting01.getSignatures(), originalSignatures);
+ assertThat(testPkgSetting01.getLongVersionCode(), is(UPDATED_VERSION_CODE));
final PackageUserState userState = testPkgSetting01.readUserState(0);
verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
false /*notLaunched*/, false /*stopped*/, true /*installed*/);
@@ -774,14 +774,14 @@
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
UUID.randomUUID());
- assertThat(testPkgSetting01.appId, is(0));
+ assertThat(testPkgSetting01.getAppId(), is(0));
assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
- assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
+ assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
assertThat(testPkgSetting01.pkgFlags, is(0));
assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
- assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
- assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86"));
- assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE));
+ assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("x86_64"));
+ assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("x86"));
+ assertThat(testPkgSetting01.getLongVersionCode(), is(INITIAL_VERSION_CODE));
// by default, the package is considered stopped
final PackageUserState userState = testPkgSetting01.readUserState(0);
verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
@@ -816,14 +816,14 @@
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
UUID.randomUUID());
- assertThat(testPkgSetting01.appId, is(10064));
+ assertThat(testPkgSetting01.getAppId(), is(10064));
assertThat(testPkgSetting01.getPath(), is(INITIAL_CODE_PATH));
- assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
+ assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
assertThat(testPkgSetting01.pkgFlags, is(0));
assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
- assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
- assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86"));
- assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE));
+ assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("x86_64"));
+ assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("x86"));
+ assertThat(testPkgSetting01.getLongVersionCode(), is(INITIAL_VERSION_CODE));
final PackageUserState userState = testPkgSetting01.readUserState(0);
verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
false /*notLaunched*/, false /*stopped*/, true /*installed*/);
@@ -834,8 +834,8 @@
public void testCreateNewSetting04() {
final PackageSetting disabledPkgSetting01 =
createPackageSetting(0 /*sharedUserId*/, 0 /*pkgFlags*/);
- disabledPkgSetting01.appId = 10064;
- final PackageSignatures disabledSignatures = disabledPkgSetting01.signatures;
+ disabledPkgSetting01.setAppId(10064);
+ final PackageSignatures disabledSignatures = disabledPkgSetting01.getSignatures();
final PackageSetting testPkgSetting01 = Settings.createNewSetting(
PACKAGE_NAME,
null /*originalPkg*/,
@@ -858,15 +858,15 @@
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
UUID.randomUUID());
- assertThat(testPkgSetting01.appId, is(10064));
+ assertThat(testPkgSetting01.getAppId(), is(10064));
assertThat(testPkgSetting01.getPath(), is(UPDATED_CODE_PATH));
- assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
+ assertThat(testPkgSetting01.getPackageName(), is(PACKAGE_NAME));
assertThat(testPkgSetting01.pkgFlags, is(0));
assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
- assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
- assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
- assertNotSame(testPkgSetting01.signatures, disabledSignatures);
- assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE));
+ assertThat(testPkgSetting01.getPrimaryCpuAbi(), is("arm64-v8a"));
+ assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("armeabi"));
+ assertNotSame(testPkgSetting01.getSignatures(), disabledSignatures);
+ assertThat(testPkgSetting01.getLongVersionCode(), is(UPDATED_VERSION_CODE));
final PackageUserState userState = testPkgSetting01.readUserState(0);
verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
false /*notLaunched*/, false /*stopped*/, true /*installed*/);
@@ -909,25 +909,25 @@
private void verifySettingCopy(PackageSetting origPkgSetting, PackageSetting testPkgSetting) {
assertThat(origPkgSetting, is(not(testPkgSetting)));
- assertThat(origPkgSetting.appId, is(testPkgSetting.appId));
+ assertThat(origPkgSetting.getAppId(), is(testPkgSetting.getAppId()));
assertSame(origPkgSetting.getPath(), testPkgSetting.getPath());
assertThat(origPkgSetting.getPath(), is(testPkgSetting.getPath()));
assertSame(origPkgSetting.getPathString(), testPkgSetting.getPathString());
assertThat(origPkgSetting.getPathString(), is(testPkgSetting.getPathString()));
- assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString);
- assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString));
+ assertSame(origPkgSetting.getCpuAbiOverride(), testPkgSetting.getCpuAbiOverride());
+ assertThat(origPkgSetting.getCpuAbiOverride(), is(testPkgSetting.getCpuAbiOverride()));
assertThat(origPkgSetting.getDomainSetId(), is(testPkgSetting.getDomainSetId()));
- assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime));
- assertSame(origPkgSetting.installSource, testPkgSetting.installSource);
- assertThat(origPkgSetting.installPermissionsFixed,
- is(testPkgSetting.installPermissionsFixed));
- assertSame(origPkgSetting.keySetData, testPkgSetting.keySetData);
- assertThat(origPkgSetting.keySetData, is(testPkgSetting.keySetData));
- assertThat(origPkgSetting.lastUpdateTime, is(testPkgSetting.lastUpdateTime));
- assertSame(origPkgSetting.legacyNativeLibraryPathString,
- testPkgSetting.legacyNativeLibraryPathString);
- assertThat(origPkgSetting.legacyNativeLibraryPathString,
- is(testPkgSetting.legacyNativeLibraryPathString));
+ assertThat(origPkgSetting.getFirstInstallTime(), is(testPkgSetting.getFirstInstallTime()));
+ assertSame(origPkgSetting.getInstallSource(), testPkgSetting.getInstallSource());
+ assertThat(origPkgSetting.isInstallPermissionsFixed(),
+ is(testPkgSetting.isInstallPermissionsFixed()));
+ assertSame(origPkgSetting.getKeySetData(), testPkgSetting.getKeySetData());
+ assertThat(origPkgSetting.getKeySetData(), is(testPkgSetting.getKeySetData()));
+ assertThat(origPkgSetting.getLastUpdateTime(), is(testPkgSetting.getLastUpdateTime()));
+ assertSame(origPkgSetting.getLegacyNativeLibraryPath(),
+ testPkgSetting.getLegacyNativeLibraryPath());
+ assertThat(origPkgSetting.getLegacyNativeLibraryPath(),
+ is(testPkgSetting.getLegacyNativeLibraryPath()));
if (origPkgSetting.mimeGroups != null) {
assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups);
}
@@ -936,31 +936,31 @@
testPkgSetting.mLegacyPermissionsState);
assertThat(origPkgSetting.mLegacyPermissionsState,
is(testPkgSetting.mLegacyPermissionsState));
- assertThat(origPkgSetting.name, is(testPkgSetting.name));
+ assertThat(origPkgSetting.getPackageName(), is(testPkgSetting.getPackageName()));
// mOldCodePaths is _not_ copied
// assertNotSame(origPkgSetting.mOldCodePaths, testPkgSetting.mOldCodePaths);
// assertThat(origPkgSetting.mOldCodePaths, is(not(testPkgSetting.mOldCodePaths)));
- assertSame(origPkgSetting.pkg, testPkgSetting.pkg);
+ assertSame(origPkgSetting.getPkg(), testPkgSetting.getPkg());
// No equals() method for this object
// assertThat(origPkgSetting.pkg, is(testPkgSetting.pkg));
assertThat(origPkgSetting.pkgFlags, is(testPkgSetting.pkgFlags));
assertThat(origPkgSetting.pkgPrivateFlags, is(testPkgSetting.pkgPrivateFlags));
- assertSame(origPkgSetting.primaryCpuAbiString, testPkgSetting.primaryCpuAbiString);
- assertThat(origPkgSetting.primaryCpuAbiString, is(testPkgSetting.primaryCpuAbiString));
- assertThat(origPkgSetting.realName, is(testPkgSetting.realName));
- assertSame(origPkgSetting.secondaryCpuAbiString, testPkgSetting.secondaryCpuAbiString);
- assertThat(origPkgSetting.secondaryCpuAbiString, is(testPkgSetting.secondaryCpuAbiString));
- assertSame(origPkgSetting.sharedUser, testPkgSetting.sharedUser);
- assertThat(origPkgSetting.sharedUser, is(testPkgSetting.sharedUser));
- assertSame(origPkgSetting.signatures, testPkgSetting.signatures);
- assertThat(origPkgSetting.signatures, is(testPkgSetting.signatures));
- assertThat(origPkgSetting.timeStamp, is(testPkgSetting.timeStamp));
+ assertSame(origPkgSetting.getPrimaryCpuAbi(), testPkgSetting.getPrimaryCpuAbi());
+ assertThat(origPkgSetting.getPrimaryCpuAbi(), is(testPkgSetting.getPrimaryCpuAbi()));
+ assertThat(origPkgSetting.getRealName(), is(testPkgSetting.getRealName()));
+ assertSame(origPkgSetting.getSecondaryCpuAbi(), testPkgSetting.getSecondaryCpuAbi());
+ assertThat(origPkgSetting.getSecondaryCpuAbi(), is(testPkgSetting.getSecondaryCpuAbi()));
+ assertSame(origPkgSetting.getSharedUser(), testPkgSetting.getSharedUser());
+ assertThat(origPkgSetting.getSharedUser(), is(testPkgSetting.getSharedUser()));
+ assertSame(origPkgSetting.getSignatures(), testPkgSetting.getSignatures());
+ assertThat(origPkgSetting.getSignatures(), is(testPkgSetting.getSignatures()));
+ assertThat(origPkgSetting.getLastModifiedTime(), is(testPkgSetting.getLastModifiedTime()));
assertNotSame(origPkgSetting.getUserState(), is(testPkgSetting.getUserState()));
// No equals() method for SparseArray object
// assertThat(origPkgSetting.getUserState(), is(testPkgSetting.getUserState()));
- assertThat(origPkgSetting.versionCode, is(testPkgSetting.versionCode));
- assertSame(origPkgSetting.volumeUuid, testPkgSetting.volumeUuid);
- assertThat(origPkgSetting.volumeUuid, is(testPkgSetting.volumeUuid));
+ assertThat(origPkgSetting.getLongVersionCode(), is(testPkgSetting.getLongVersionCode()));
+ assertSame(origPkgSetting.getVolumeUuid(), testPkgSetting.getVolumeUuid());
+ assertThat(origPkgSetting.getVolumeUuid(), is(testPkgSetting.getVolumeUuid()));
}
private SharedUserSetting createSharedUserSetting(Settings settings, String userName,
@@ -1244,14 +1244,14 @@
/* verify packages have been given the appropriate information */
PackageSetting ps = packages.get("com.android.app1");
- assertThat(ps.keySetData.getProperSigningKeySet(), is(1L));
+ assertThat(ps.getKeySetData().getProperSigningKeySet(), is(1L));
ps = packages.get("com.android.app2");
- assertThat(ps.keySetData.getProperSigningKeySet(), is(1L));
- assertThat(ps.keySetData.getAliases().get("AB"), is(4L));
+ assertThat(ps.getKeySetData().getProperSigningKeySet(), is(1L));
+ assertThat(ps.getKeySetData().getAliases().get("AB"), is(4L));
ps = packages.get("com.android.app3");
- assertThat(ps.keySetData.getProperSigningKeySet(), is(2L));
- assertThat(ps.keySetData.getAliases().get("C"), is(3L));
- assertThat(ps.keySetData.getUpgradeKeySets().length, is(1));
- assertThat(ps.keySetData.getUpgradeKeySets()[0], is(3L));
+ assertThat(ps.getKeySetData().getProperSigningKeySet(), is(2L));
+ assertThat(ps.getKeySetData().getAliases().get("C"), is(3L));
+ assertThat(ps.getKeySetData().getUpgradeKeySets().length, is(1));
+ assertThat(ps.getKeySetData().getUpgradeKeySets()[0], is(3L));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 0cbac87..4113332 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -850,7 +850,7 @@
assertEquals(a.theme, that.theme);
assertEquals(a.fullBackupContent, that.fullBackupContent);
assertEquals(a.uiOptions, that.uiOptions);
- assertEquals(a.flags, that.flags);
+ assertEquals(Integer.toBinaryString(a.flags), Integer.toBinaryString(that.flags));
assertEquals(a.privateFlags, that.privateFlags);
assertEquals(a.requiresSmallestWidthDp, that.requiresSmallestWidthDp);
assertEquals(a.compatibleWidthLimitDp, that.compatibleWidthLimitDp);
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index f551ad1..93aae28 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -175,14 +175,14 @@
mSecondaryCpuAbiString, mCpuAbiOverrideString, mPVersionCode, mPkgFlags,
mPrivateFlags, mSharedUserId, mUsesStaticLibraries, mUsesStaticLibrariesVersions,
mMimeGroups, mDomainSetId);
- packageSetting.signatures = mSigningDetails != null
+ packageSetting.setSignatures(mSigningDetails != null
? new PackageSignatures(mSigningDetails)
- : new PackageSignatures();
- packageSetting.pkg = mPkg;
- packageSetting.appId = mAppId;
- packageSetting.volumeUuid = this.mVolumeUuid;
+ : new PackageSignatures());
+ packageSetting.setPkg(mPkg);
+ packageSetting.setAppId(mAppId);
+ packageSetting.setVolumeUuid(this.mVolumeUuid);
if (mInstallSource != null) {
- packageSetting.installSource = mInstallSource;
+ packageSetting.setInstallSource(mInstallSource);
}
for (int i = 0; i < mUserStates.size(); i++) {
packageSetting.setUserState(mUserStates.keyAt(i), mUserStates.valueAt(i));
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index b9431bf..b6d4b31 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -138,11 +138,11 @@
// List.
TypedXmlPullParser parser = getXMLFromResources("xml/one-signer.xml");
ArrayList<Signature> signatures = new ArrayList<>();
- mPackageSetting.signatures.readXml(parser, signatures);
+ mPackageSetting.getSignatures().readXml(parser, signatures);
Set<String> expectedSignatures = createSetOfSignatures(FIRST_EXPECTED_SIGNATURE);
verifySignaturesContainExpectedValues(signatures, expectedSignatures);
parser = getXMLFromResources("xml/one-signer-previous-cert.xml");
- mPackageSetting.signatures.readXml(parser, signatures);
+ mPackageSetting.getSignatures().readXml(parser, signatures);
expectedSignatures = createSetOfSignatures(FIRST_EXPECTED_SIGNATURE);
verifySignaturesContainExpectedValues(signatures, expectedSignatures);
}
@@ -166,11 +166,11 @@
TypedXmlPullParser parser = getXMLFromResources(
"xml/one-signer-invalid-public-key-cert-key.xml");
ArrayList<Signature> signatures = new ArrayList<>();
- mPackageSetting.signatures.readXml(parser, signatures);
+ mPackageSetting.getSignatures().readXml(parser, signatures);
assertEquals(
"The signing details was not UNKNOWN after parsing an invalid public key cert key"
+ " attribute",
- SigningDetails.UNKNOWN, mPackageSetting.signatures.mSigningDetails);
+ SigningDetails.UNKNOWN, mPackageSetting.getSigningDetails());
}
@Test
@@ -352,7 +352,7 @@
// with the flags set to 0 does not have any capabilities.
TypedXmlPullParser parser = getXMLFromResources("xml/two-signers-in-lineage-no-caps.xml");
ArrayList<Signature> signatures = new ArrayList<>();
- mPackageSetting.signatures.readXml(parser, signatures);
+ mPackageSetting.getSignatures().readXml(parser, signatures);
// obtain the Signature in the list matching the previous signing certificate
Signature previousSignature = null;
for (Signature signature : signatures) {
@@ -365,7 +365,7 @@
assertNotNull("Unable to find the expected previous signer", previousSignature);
for (int capability : CAPABILITIES) {
assertFalse("The previous signer should not have the " + capability + " capability",
- mPackageSetting.signatures.mSigningDetails.hasCertificate(previousSignature,
+ mPackageSetting.getSigningDetails().hasCertificate(previousSignature,
capability));
}
}
@@ -378,12 +378,12 @@
String... expectedSignatureValues) throws Exception {
TypedXmlPullParser parser = getXMLFromResources(xmlFile);
ArrayList<Signature> signatures = new ArrayList<>();
- mPackageSetting.signatures.readXml(parser, signatures);
+ mPackageSetting.getSignatures().readXml(parser, signatures);
Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
verifySignaturesContainExpectedValues(signatures, expectedSignatures);
assertEquals("The returned signature scheme is not the expected value",
expectedSchemeVersion,
- mPackageSetting.signatures.mSigningDetails.getSignatureSchemeVersion());
+ mPackageSetting.getSigningDetails().getSignatureSchemeVersion());
}
/**
@@ -395,11 +395,11 @@
int schemeVersion, String... expectedSignatureValues) throws Exception {
TypedXmlPullParser parser = getXMLFromResources(xmlFile);
ArrayList<Signature> signatures = new ArrayList<>();
- mPackageSetting.signatures.readXml(parser, signatures);
+ mPackageSetting.getSignatures().readXml(parser, signatures);
Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
verifySignaturesContainExpectedValues(signatures, expectedSignatures);
assertEquals("The returned signature scheme is not the expected value", schemeVersion,
- mPackageSetting.signatures.mSigningDetails.getSignatureSchemeVersion());
+ mPackageSetting.getSigningDetails().getSignatureSchemeVersion());
for (Signature signature : signatures) {
String signatureValue = HexDump.toHexString(signature.toByteArray(), false);
int expectedCapabilities = SIGNATURE_TO_CAPABILITY_MAP.get(signatureValue);
@@ -407,7 +407,7 @@
+ " was not found with the expected capabilities of " +
expectedCapabilities
+ " in the signing details",
- mPackageSetting.signatures.mSigningDetails.hasCertificate(signature,
+ mPackageSetting.getSigningDetails().hasCertificate(signature,
expectedCapabilities));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index 1973410..370b1c9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -86,7 +86,7 @@
@Mock
PlatformCompat mMockCompatibility;
@Mock
- PackageManagerService.Injector mMockInjector;
+ PackageManagerServiceInjector mMockInjector;
@Mock
PackageManagerService mMockPackageManager;
@@ -171,7 +171,7 @@
final ScanResult scanResult = executeScan(scanRequest);
- assertThat(scanResult.mPkgSetting.realName, is("com.package.real"));
+ assertThat(scanResult.mPkgSetting.getRealName(), is("com.package.real"));
final ScanRequest scanRequestNoRealPkg =
createBasicScanRequestBuilder(
@@ -180,7 +180,7 @@
.build();
final ScanResult scanResultNoReal = executeScan(scanRequestNoRealPkg);
- assertThat(scanResultNoReal.mPkgSetting.realName, nullValue());
+ assertThat(scanResultNoReal.mPkgSetting.getRealName(), nullValue());
}
@Test
@@ -207,9 +207,9 @@
assertBasicPackageScanResult(scanResult, DUMMY_PACKAGE_NAME, false /*isInstant*/);
- assertThat(scanResult.mPkgSetting.primaryCpuAbiString, is("primaryCpuAbi"));
- assertThat(scanResult.mPkgSetting.secondaryCpuAbiString, is("secondaryCpuAbi"));
- assertThat(scanResult.mPkgSetting.cpuAbiOverrideString, nullValue());
+ assertThat(scanResult.mPkgSetting.getPrimaryCpuAbi(), is("primaryCpuAbi"));
+ assertThat(scanResult.mPkgSetting.getSecondaryCpuAbi(), is("secondaryCpuAbi"));
+ assertThat(scanResult.mPkgSetting.getCpuAbiOverride(), nullValue());
assertPathsNotDerived(scanResult);
}
@@ -326,7 +326,7 @@
final ScanResult scanResult = executeScan(
new ScanRequestBuilder(basicPackage).setPkgSetting(pkgSetting).build());
- assertThat(scanResult.mPkgSetting.volumeUuid, is(UUID_TWO.toString()));
+ assertThat(scanResult.mPkgSetting.getVolumeUuid(), is(UUID_TWO.toString()));
}
@Test
@@ -458,7 +458,7 @@
final ScanResult scanResult = executeScan(scanRequest);
- assertThat(scanResult.mPkgSetting.installSource.isOrphaned, is(true));
+ assertThat(scanResult.mPkgSetting.getInstallSource().isOrphaned, is(true));
}
private static Matcher<Integer> hasFlag(final int flag) {
@@ -492,7 +492,7 @@
// Need to call hideAsFinal to cache derived fields. This is normally done in PMS, but not
// in this cut down flow used for the test.
- ((ParsedPackage) result.mPkgSetting.pkg).hideAsFinal();
+ ((ParsedPackage) result.mPkgSetting.getPkg()).hideAsFinal();
return result;
}
@@ -540,20 +540,21 @@
assertBasicPackageSetting(scanResult, packageName, isInstant, pkgSetting);
final ApplicationInfo applicationInfo = PackageInfoUtils.generateApplicationInfo(
- pkgSetting.pkg, 0, pkgSetting.readUserState(0), 0, pkgSetting);
+ pkgSetting.getPkg(), 0, pkgSetting.readUserState(0), 0, pkgSetting);
assertBasicApplicationInfo(scanResult, applicationInfo);
}
private static void assertBasicPackageSetting(ScanResult scanResult,
String packageName, boolean isInstant, PackageSetting pkgSetting) {
- assertThat(pkgSetting.pkg.getPackageName(), is(packageName));
+ assertThat(pkgSetting.getPkg().getPackageName(), is(packageName));
assertThat(pkgSetting.getInstantApp(0), is(isInstant));
assertThat(pkgSetting.usesStaticLibraries,
arrayContaining("some.static.library", "some.other.static.library"));
assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L}));
- assertThat(pkgSetting.pkg, is(scanResult.mRequest.mParsedPackage));
+ assertThat(pkgSetting.getPkg(), is(scanResult.mRequest.mParsedPackage));
assertThat(pkgSetting.getPath(), is(new File(createCodePath(packageName))));
- assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345)));
+ assertThat(pkgSetting.getLongVersionCode(),
+ is(PackageInfo.composeLongVersionCode(1, 2345)));
}
private static void assertBasicApplicationInfo(ScanResult scanResult,
@@ -574,12 +575,12 @@
private static void assertAbiAndPathssDerived(ScanResult scanResult) {
PackageSetting pkgSetting = scanResult.mPkgSetting;
final ApplicationInfo applicationInfo = PackageInfoUtils.generateApplicationInfo(
- pkgSetting.pkg, 0, pkgSetting.readUserState(0), 0, pkgSetting);
+ pkgSetting.getPkg(), 0, pkgSetting.readUserState(0), 0, pkgSetting);
assertThat(applicationInfo.primaryCpuAbi, is("derivedPrimary"));
assertThat(applicationInfo.secondaryCpuAbi, is("derivedSecondary"));
assertThat(applicationInfo.nativeLibraryRootDir, is("derivedRootDir"));
- assertThat(pkgSetting.legacyNativeLibraryPathString, is("derivedRootDir"));
+ assertThat(pkgSetting.getLegacyNativeLibraryPath(), is("derivedRootDir"));
assertThat(applicationInfo.nativeLibraryRootRequiresIsa, is(true));
assertThat(applicationInfo.nativeLibraryDir, is("derivedNativeDir"));
assertThat(applicationInfo.secondaryNativeLibraryDir, is("derivedNativeDir2"));
@@ -588,9 +589,9 @@
private static void assertPathsNotDerived(ScanResult scanResult) {
PackageSetting pkgSetting = scanResult.mPkgSetting;
final ApplicationInfo applicationInfo = PackageInfoUtils.generateApplicationInfo(
- pkgSetting.pkg, 0, pkgSetting.readUserState(0), 0, pkgSetting);
+ pkgSetting.getPkg(), 0, pkgSetting.readUserState(0), 0, pkgSetting);
assertThat(applicationInfo.nativeLibraryRootDir, is("getRootDir"));
- assertThat(pkgSetting.legacyNativeLibraryPathString, is("getRootDir"));
+ assertThat(pkgSetting.getLegacyNativeLibraryPath(), is("getRootDir"));
assertThat(applicationInfo.nativeLibraryRootRequiresIsa, is(true));
assertThat(applicationInfo.nativeLibraryDir, is("getNativeDir"));
assertThat(applicationInfo.secondaryNativeLibraryDir, is("getNativeDir2"));
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
index ae07f8e..5d3905a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingTestBase.kt
@@ -184,6 +184,8 @@
whenever(pkgState) { PackageStateUnserialized() }
whenever(readUserState(anyInt())) { dummyUserState }
whenever(categoryOverride) { ApplicationInfo.CATEGORY_UNDEFINED }
+ whenever(primaryCpuAbi) { null }
+ whenever(secondaryCpuAbi) { null }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
new file mode 100644
index 0000000..1d9ea4b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/ApexSharedLibraryUpdaterTest.java
@@ -0,0 +1,281 @@
+/*
+ * 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.pm.parsing.library;
+
+import android.os.Build;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.SystemConfig;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.PackageImpl;
+import com.android.server.pm.parsing.pkg.ParsedPackage;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+
+/**
+ * Test for {@link ApexSharedLibraryUpdater}
+ */
+@Presubmit
+@SmallTest
+@RunWith(JUnit4.class)
+public class ApexSharedLibraryUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+ private final ArrayMap<String, SystemConfig.SharedLibraryEntry> mSharedLibraries =
+ new ArrayMap<>(8);
+
+ @Before
+ public void setUp() throws Exception {
+ installSharedLibraries();
+ }
+
+ private void installSharedLibraries() throws Exception {
+ mSharedLibraries.clear();
+ insertLibrary("foo", 0, 0);
+ insertLibrary("fooBcpSince30", 30, 0);
+ insertLibrary("fooBcpBefore30", 0, 30);
+ insertLibrary("fooFromFuture", Build.VERSION.SDK_INT + 2, 0);
+ }
+
+ private void insertLibrary(String libraryName, int onBootclasspathSince,
+ int onBootclasspathBefore) {
+ mSharedLibraries.put(libraryName, new SystemConfig.SharedLibraryEntry(
+ libraryName,
+ "foo.jar",
+ new String[0] /* dependencies */,
+ onBootclasspathSince,
+ onBootclasspathBefore
+ )
+ );
+ }
+
+ @Test
+ public void testRegularAppOnRPlus() {
+ // platform Q should have changes (tested below)
+
+ // these should have no changes
+ checkNoChanges(Build.VERSION_CODES.R);
+ checkNoChanges(Build.VERSION_CODES.S);
+ checkNoChanges(Build.VERSION_CODES.TIRAMISU);
+ checkNoChanges(Build.VERSION_CODES.CUR_DEVELOPMENT);
+ }
+
+ private void checkNoChanges(int targetSdkVersion) {
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(targetSdkVersion)
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(targetSdkVersion)
+ .hideAsParsed())
+ .hideAsFinal();
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void testBcpSince30Applied() {
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .addUsesLibrary("fooBcpSince30")
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .hideAsParsed())
+ .hideAsFinal();
+
+ // note: target sdk is not what matters in this logic. It's the system SDK
+ // should be removed because on 30+ (R+) it is implicit
+
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void testBcpSince11kNotAppliedWithoutLibrary() {
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .hideAsParsed())
+ .hideAsFinal();
+
+ // note: target sdk is not what matters in this logic. It's the system SDK
+ // nothing should change because the implicit from is only from a future platform release
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void testBcpSince11kNotAppliedWithLibrary() {
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .addUsesLibrary("fooFromFuture")
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .addUsesLibrary("fooFromFuture")
+ .hideAsParsed())
+ .hideAsFinal();
+
+ // note: target sdk is not what matters in this logic. It's the system SDK
+ // nothing should change because the implicit from is only from a future platform release
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void testBcpBefore30NotApplied() {
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .hideAsParsed())
+ .hideAsFinal();
+
+ // should not be affected because it is still in the BCP in 30 / R
+ checkBackwardsCompatibility(before, after);
+ }
+
+ @Test
+ public void testBcpBefore30Applied() {
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.Q)
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.Q)
+ .addUsesLibrary("fooBcpBefore30")
+ .hideAsParsed())
+ .hideAsFinal();
+
+ // should be present because this was in BCP in 29 / Q
+ checkBackwardsCompatibility(before, after);
+ }
+
+ /**
+ * Test a library that was first removed from the BCP [to a mainline module] and later was
+ * moved back to the BCP via a mainline module update. All of this happening before the current
+ * SDK.
+ */
+ @Test
+ public void testBcpRemovedThenAddedPast() {
+ insertLibrary("fooBcpRemovedThenAdded", 30, 28);
+
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.N)
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.N)
+ .addUsesLibrary("fooBcpBefore30")
+ .hideAsParsed())
+ .hideAsFinal();
+
+ // the library is now in the BOOTCLASSPATH (for the second time) so it doesn't need to be
+ // listed
+ checkBackwardsCompatibility(before, after);
+ }
+
+ /**
+ * Test a library that was first removed from the BCP [to a mainline module] and later was
+ * moved back to the BCP via a mainline module update. The first part happening before the
+ * current SDK and the second part after.
+ */
+ @Test
+ public void testBcpRemovedThenAddedMiddle_targetQ() {
+ insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);
+
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.Q)
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.Q)
+ .addUsesLibrary("fooBcpRemovedThenAdded")
+ .addUsesLibrary("fooBcpBefore30")
+ .hideAsParsed())
+ .hideAsFinal();
+
+ // in this example, we are at the point where the library is not in the BOOTCLASSPATH.
+ // Because the app targets Q / 29 (when this library was in the BCP) then we need to add it
+ checkBackwardsCompatibility(before, after);
+ }
+
+ /**
+ * Test a library that was first removed from the BCP [to a mainline module] and later was
+ * moved back to the BCP via a mainline module update. The first part happening before the
+ * current SDK and the second part after.
+ */
+ @Test
+ public void testBcpRemovedThenAddedMiddle_targetR() {
+ insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);
+
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .hideAsParsed())
+ .hideAsFinal();
+
+ // in this example, we are at the point where the library is not in the BOOTCLASSPATH.
+ // Because the app targets R/30 (when this library was removed from the BCP) then we don't
+ //need to add it
+ checkBackwardsCompatibility(before, after);
+ }
+
+ /**
+ * Test a library that was first removed from the BCP [to a mainline module] and later was
+ * moved back to the BCP via a mainline module update. The first part happening before the
+ * current SDK and the second part after.
+ */
+ @Test
+ public void testBcpRemovedThenAddedMiddle_targetR_usingLib() {
+ insertLibrary("fooBcpRemovedThenAdded", Build.VERSION.SDK_INT + 1, 30);
+
+ ParsedPackage before = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .addUsesLibrary("fooBcpRemovedThenAdded")
+ .hideAsParsed());
+
+ AndroidPackage after = ((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME)
+ .setTargetSdkVersion(Build.VERSION_CODES.R)
+ .addUsesLibrary("fooBcpRemovedThenAdded")
+ .hideAsParsed())
+ .hideAsFinal();
+
+ // in this example, we are at the point where the library is not in the BOOTCLASSPATH.
+ // Because the app wants to use the library, it needs to be present
+ checkBackwardsCompatibility(before, after);
+ }
+
+ private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
+ checkBackwardsCompatibility(before, after,
+ () -> new ApexSharedLibraryUpdater(mSharedLibraries));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
index 9768f17..5bcd0f6 100644
--- a/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/parsing/library/PackageBackwardCompatibilityTest.java
@@ -21,6 +21,8 @@
import static com.android.server.pm.parsing.library.SharedLibraryNames.ANDROID_TEST_RUNNER;
import static com.android.server.pm.parsing.library.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY;
+import static com.google.common.truth.Truth.assertThat;
+
import android.content.pm.parsing.ParsingPackage;
import android.os.Build;
import android.platform.test.annotations.Presubmit;
@@ -182,6 +184,22 @@
checkBackwardsCompatibility(before, ((ParsedPackage) after.hideAsParsed()).hideAsFinal());
}
+ /**
+ * Ensures that ApexSharedLibraryUpdater is the last updater in the list of package updaters
+ * used by PackageBackwardCompatibility.
+ *
+ * This is required so mainline can add and remove libraries installed by the platform updaters.
+ */
+ @Test
+ public void testApexPackageUpdaterOrdering() {
+ PackageBackwardCompatibility instance =
+ (PackageBackwardCompatibility) PackageBackwardCompatibility.getInstance();
+ PackageSharedLibraryUpdater[] updaterArray = instance.getPackageUpdaters();
+
+ PackageSharedLibraryUpdater lastUpdater = updaterArray[updaterArray.length - 1];
+ assertThat(lastUpdater).isInstanceOf(ApexSharedLibraryUpdater.class);
+ }
+
private void checkBackwardsCompatibility(ParsedPackage before, AndroidPackage after) {
checkBackwardsCompatibility(before, after, PackageBackwardCompatibility::getInstance);
}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
index 39815b7..43d646a 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -46,9 +46,7 @@
import android.os.HidlMemoryUtil;
import android.os.ParcelFileDescriptor;
import android.os.SharedMemory;
-import android.system.ErrnoException;
-import java.io.FileDescriptor;
import java.nio.ByteBuffer;
import java.util.List;
@@ -69,7 +67,7 @@
model.uuid = "12345678-2345-3456-4567-abcdef987654";
model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
byte[] data = new byte[]{91, 92, 93, 94, 95};
- model.data = new ParcelFileDescriptor(byteArrayToFileDescriptor(data));
+ model.data = byteArrayToParcelFileDescriptor(data);
model.dataSize = data.length;
return model;
}
@@ -449,13 +447,12 @@
assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
}
- private static FileDescriptor byteArrayToFileDescriptor(byte[] data) {
- try {
- SharedMemory shmem = SharedMemory.create("", data.length);
+ private static ParcelFileDescriptor byteArrayToParcelFileDescriptor(byte[] data) {
+ try (SharedMemory shmem = SharedMemory.create("", data.length)) {
ByteBuffer buffer = shmem.mapReadWrite();
buffer.put(data);
- return shmem.getFileDescriptor();
- } catch (ErrnoException e) {
+ return shmem.getFdDup();
+ } catch (Exception e) {
throw new RuntimeException(e);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/UptimeTimerTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/UptimeTimerTest.java
new file mode 100644
index 0000000..38297bf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/UptimeTimerTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.soundtrigger_middleware;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+@RunWith(AndroidJUnit4.class)
+public class UptimeTimerTest {
+ private static final String TAG = "UptimeTimerTest";
+
+ @Test
+ public void testBasic() throws InterruptedException {
+ AtomicBoolean taskRan = new AtomicBoolean(false);
+ UptimeTimer timer = new UptimeTimer("TestTimer");
+ timer.createTask(() -> taskRan.set(true), 100);
+ Thread.sleep(50);
+ boolean before = taskRan.get();
+ Thread.sleep(100);
+ boolean after = taskRan.get();
+ assertFalse(before);
+ assertTrue(after);
+ }
+
+ @Test
+ public void testCancel() throws InterruptedException {
+ AtomicBoolean taskRan = new AtomicBoolean(false);
+ UptimeTimer timer = new UptimeTimer("TestTimer");
+ UptimeTimer.Task task = timer.createTask(() -> taskRan.set(true), 100);
+ Thread.sleep(50);
+ boolean before = taskRan.get();
+ task.cancel();
+ Thread.sleep(100);
+ boolean after = taskRan.get();
+ assertFalse(before);
+ assertFalse(after);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index cc53160..4dcd633 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertEquals;
import static org.testng.Assert.expectThrows;
+import android.os.Build;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -330,6 +331,164 @@
}
/**
+ * Tests that readPermissions works correctly for a library with on-bootclasspath-before
+ * and on-bootclasspath-since.
+ */
+ @Test
+ public void readPermissions_allowLibs_parsesSimpleLibrary() throws IOException {
+ String contents =
+ "<permissions>\n"
+ + " <library \n"
+ + " name=\"foo\"\n"
+ + " file=\"foo.jar\"\n"
+ + " on-bootclasspath-before=\"10\"\n"
+ + " on-bootclasspath-since=\"20\"\n"
+ + " />\n\n"
+ + " </permissions>";
+ parseSharedLibraries(contents);
+ assertFooIsOnlySharedLibrary();
+ SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
+ assertThat(entry.onBootclasspathBefore).isEqualTo(10);
+ assertThat(entry.onBootclasspathSince).isEqualTo(20);
+ }
+
+ /**
+ * Tests that readPermissions works correctly for a library using the new
+ * {@code updatable-library} tag.
+ */
+ @Test
+ public void readPermissions_allowLibs_parsesUpdatableLibrary() throws IOException {
+ String contents =
+ "<permissions>\n"
+ + " <updatable-library \n"
+ + " name=\"foo\"\n"
+ + " file=\"foo.jar\"\n"
+ + " on-bootclasspath-before=\"10\"\n"
+ + " on-bootclasspath-since=\"20\"\n"
+ + " />\n\n"
+ + " </permissions>";
+ parseSharedLibraries(contents);
+ assertFooIsOnlySharedLibrary();
+ SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
+ assertThat(entry.onBootclasspathBefore).isEqualTo(10);
+ assertThat(entry.onBootclasspathSince).isEqualTo(20);
+ }
+
+ /**
+ * Tests that readPermissions for a library with {@code min-device-sdk} lower than the current
+ * SDK results in the library being added to the shared libraries.
+ */
+ @Test
+ public void readPermissions_allowLibs_allowsOldMinSdk() throws IOException {
+ String contents =
+ "<permissions>\n"
+ + " <library \n"
+ + " name=\"foo\"\n"
+ + " file=\"foo.jar\"\n"
+ + " min-device-sdk=\"30\"\n"
+ + " />\n\n"
+ + " </permissions>";
+ parseSharedLibraries(contents);
+ assertFooIsOnlySharedLibrary();
+ }
+
+ /**
+ * Tests that readPermissions for a library with {@code min-device-sdk} equal to the current
+ * SDK results in the library being added to the shared libraries.
+ */
+ @Test
+ public void readPermissions_allowLibs_allowsCurrentMinSdk() throws IOException {
+ String contents =
+ "<permissions>\n"
+ + " <library \n"
+ + " name=\"foo\"\n"
+ + " file=\"foo.jar\"\n"
+ + " min-device-sdk=\"" + Build.VERSION.SDK_INT + "\"\n"
+ + " />\n\n"
+ + " </permissions>";
+ parseSharedLibraries(contents);
+ assertFooIsOnlySharedLibrary();
+ }
+
+ /**
+ * Tests that readPermissions for a library with {@code min-device-sdk} greater than the current
+ * SDK results in the library being ignored.
+ */
+ @Test
+ public void readPermissions_allowLibs_ignoresMinSdkInFuture() throws IOException {
+ String contents =
+ "<permissions>\n"
+ + " <library \n"
+ + " name=\"foo\"\n"
+ + " file=\"foo.jar\"\n"
+ + " min-device-sdk=\"" + (Build.VERSION.SDK_INT + 1) + "\"\n"
+ + " />\n\n"
+ + " </permissions>";
+ parseSharedLibraries(contents);
+ assertThat(mSysConfig.getSharedLibraries()).isEmpty();
+ }
+
+ /**
+ * Tests that readPermissions for a library with {@code max-device-sdk} less than the current
+ * SDK results in the library being ignored.
+ */
+ @Test
+ public void readPermissions_allowLibs_ignoredOldMaxSdk() throws IOException {
+ String contents =
+ "<permissions>\n"
+ + " <library \n"
+ + " name=\"foo\"\n"
+ + " file=\"foo.jar\"\n"
+ + " max-device-sdk=\"30\"\n"
+ + " />\n\n"
+ + " </permissions>";
+ parseSharedLibraries(contents);
+ assertThat(mSysConfig.getSharedLibraries()).isEmpty();
+ }
+
+ /**
+ * Tests that readPermissions for a library with {@code max-device-sdk} equal to the current
+ * SDK results in the library being added to the shared libraries.
+ */
+ @Test
+ public void readPermissions_allowLibs_allowsCurrentMaxSdk() throws IOException {
+ String contents =
+ "<permissions>\n"
+ + " <library \n"
+ + " name=\"foo\"\n"
+ + " file=\"foo.jar\"\n"
+ + " max-device-sdk=\"" + Build.VERSION.SDK_INT + "\"\n"
+ + " />\n\n"
+ + " </permissions>";
+ parseSharedLibraries(contents);
+ assertFooIsOnlySharedLibrary();
+ }
+
+ /**
+ * Tests that readPermissions for a library with {@code max-device-sdk} greater than the current
+ * SDK results in the library being added to the shared libraries.
+ */
+ @Test
+ public void readPermissions_allowLibs_allowsMaxSdkInFuture() throws IOException {
+ String contents =
+ "<permissions>\n"
+ + " <library \n"
+ + " name=\"foo\"\n"
+ + " file=\"foo.jar\"\n"
+ + " max-device-sdk=\"" + (Build.VERSION.SDK_INT + 1) + "\"\n"
+ + " />\n\n"
+ + " </permissions>";
+ parseSharedLibraries(contents);
+ assertFooIsOnlySharedLibrary();
+ }
+
+ private void parseSharedLibraries(String contents) throws IOException {
+ File folder = createTempSubfolder("permissions_folder");
+ createTempFile(folder, "permissions.xml", contents);
+ readPermissions(folder, /* permissionFlag = ALLOW_LIBS */ 0x02);
+ }
+
+ /**
* Creates folderName/fileName in the mTemporaryFolder and fills it with the contents.
*
* @param folderName subdirectory of mTemporaryFolder to put the file, creating if needed
@@ -366,4 +525,11 @@
return folder;
}
+
+ private void assertFooIsOnlySharedLibrary() {
+ assertThat(mSysConfig.getSharedLibraries().size()).isEqualTo(1);
+ SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
+ assertThat(entry.name).isEqualTo("foo");
+ assertThat(entry.filename).isEqualTo("foo.jar");
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index c434b13..65733d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2700,8 +2700,8 @@
final WindowState startingWindow = createWindowState(
new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING), activity1);
activity1.addWindow(startingWindow);
- activity1.attachStartingWindow(startingWindow);
activity1.mStartingData = mock(StartingData.class);
+ activity1.attachStartingWindow(startingWindow);
final Task task = activity1.getTask();
final Rect taskBounds = task.getBounds();
final int width = taskBounds.width();
@@ -2729,6 +2729,10 @@
assertTrue(activity2.isResizeable());
activity1.reparent(taskFragment1, POSITION_TOP);
+ verify(activity1.getSyncTransaction()).reparent(eq(startingWindow.mSurfaceControl),
+ eq(task.mSurfaceControl));
+ assertEquals(activity1.mStartingData, startingWindow.mStartingData);
+ assertEquals(task.mSurfaceControl, startingWindow.getAnimationLeashParent());
assertEquals(task, activity1.mStartingData.mAssociatedTask);
assertEquals(taskFragment1.getBounds(), activity1.getBounds());
// The activity was resized by task fragment, but starting window must still cover the task.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index 764f63d..5de4fcb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -33,6 +33,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -748,6 +749,92 @@
assertFalse(wpcAfterConfigChange2.getConfiguration().isNightModeActive());
}
+ @Test
+ public void testPackageConfigUpdate_localesNotSet_localeConfigRetrievedNull() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true,
+ DEFAULT_USER_ID);
+ WindowProcessController wpc = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), wpc);
+ mAtm.mInternal.onProcessAdded(wpc);
+
+ ActivityTaskManagerInternal.PackageConfig appSpecificConfig = mAtm.mInternal
+ .getApplicationConfig(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ // when no configuration is set we get a null object.
+ assertNull(appSpecificConfig);
+
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID);
+ packageConfigUpdater.setNightMode(Configuration.UI_MODE_NIGHT_YES).commit();
+
+ ActivityTaskManagerInternal.PackageConfig appSpecificConfig2 = mAtm.mInternal
+ .getApplicationConfig(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertNotNull(appSpecificConfig2);
+ assertNull(appSpecificConfig2.mLocales);
+ assertEquals(appSpecificConfig2.mNightMode.intValue(), Configuration.UI_MODE_NIGHT_YES);
+ }
+
+ @Test
+ public void testPackageConfigUpdate_appNotRunning_configSuccessfullyApplied() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true,
+ DEFAULT_USER_ID);
+
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID);
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB")).commit();
+
+ // Verifies if the persisted app-specific configuration is same as the committed
+ // configuration.
+ ActivityTaskManagerInternal.PackageConfig appSpecificConfig = mAtm.mInternal
+ .getApplicationConfig(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertNotNull(appSpecificConfig);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB"), appSpecificConfig.mLocales);
+
+ // Verifies if the persisted configuration for an arbitrary app is applied correctly when
+ // a new WindowProcessController is created for it.
+ WindowProcessController wpcAfterConfigChange = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpcAfterConfigChange.getConfiguration().getLocales());
+ }
+
+ @Test
+ public void testPackageConfigUpdate_appRunning_configSuccessfullyApplied() {
+ Configuration config = mAtm.getGlobalConfiguration();
+ config.setLocales(LocaleList.forLanguageTags("en-XC"));
+ mAtm.updateGlobalConfigurationLocked(config, true, true,
+ DEFAULT_USER_ID);
+ WindowProcessController wpc = createWindowProcessController(
+ DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+ mAtm.mProcessMap.put(Binder.getCallingPid(), wpc);
+ mAtm.mInternal.onProcessAdded(wpc);
+
+ ActivityTaskManagerInternal.PackageConfigurationUpdater packageConfigUpdater =
+ mAtm.mInternal.createPackageConfigurationUpdater(DEFAULT_PACKAGE_NAME,
+ DEFAULT_USER_ID);
+
+ packageConfigUpdater.setLocales(LocaleList.forLanguageTags("en-XA,ar-XB")).commit();
+
+ ActivityTaskManagerInternal.PackageConfig appSpecificConfig = mAtm.mInternal
+ .getApplicationConfig(DEFAULT_PACKAGE_NAME, DEFAULT_USER_ID);
+
+ // Verifies if the persisted app-specific configuration is same as the committed
+ // configuration.
+ assertNotNull(appSpecificConfig);
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB"), appSpecificConfig.mLocales);
+
+ // Verifies if the committed configuration is successfully applied to the required
+ // application while it is currently running.
+ assertEquals(LocaleList.forLanguageTags("en-XA,ar-XB,en-XC"),
+ wpc.getConfiguration().getLocales());
+ }
+
private WindowProcessController createWindowProcessController(String packageName,
int userId) {
WindowProcessListener mMockListener = Mockito.mock(WindowProcessListener.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index d6d7f07..5fa76bb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -27,6 +27,8 @@
import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -44,6 +46,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.annotation.Nullable;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -763,59 +766,148 @@
}
@Test
- public void testGetRemoteAnimationOverrideTaskFragmentOrganizer() {
- // TaskFragmentOrganizer registers remote animation.
+ public void testOverrideTaskFragmentAdapter_overrideWithEmbeddedActivity() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
- final ITaskFragmentOrganizer iOrganizer =
- ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
- final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
new TestRemoteAnimationRunner(), 10, 1);
- definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter);
- mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
- mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition);
+ setupTaskFragmentRemoteAnimation(organizer, adapter);
// Create a TaskFragment with embedded activity.
- final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
- .setParentTask(createTask(mDisplayContent))
- .createActivityCount(1)
- .setOrganizer(organizer)
- .build();
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(
+ createTask(mDisplayContent), organizer);
final ActivityRecord activity = taskFragment.getTopMostActivity();
activity.allDrawn = true;
spyOn(mDisplayContent.mAppTransition);
- // Prepare a transition for TaskFragment.
- mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0);
- mDisplayContent.mOpeningApps.add(activity);
- mDisplayContent.mChangingContainers.add(taskFragment);
- mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+ // Prepare a transition.
+ prepareAndTriggerAppTransition(activity, null /* closingActivity */, taskFragment);
- // Check if the transition has been overridden.
+ // Should be overridden.
verify(mDisplayContent.mAppTransition)
.overridePendingAppTransitionRemote(adapter, false /* sync */);
}
@Test
+ public void testOverrideTaskFragmentAdapter_overrideWithNonEmbeddedActivity() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+ new TestRemoteAnimationRunner(), 10, 1);
+ setupTaskFragmentRemoteAnimation(organizer, adapter);
+
+ final Task task = createTask(mDisplayContent);
+ // Closing non-embedded activity.
+ final ActivityRecord closingActivity = createActivityRecord(task);
+ closingActivity.allDrawn = true;
+ // Opening TaskFragment with embedded activity.
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
+ openingActivity.allDrawn = true;
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare a transition.
+ prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
+
+ // Should be overridden.
+ verify(mDisplayContent.mAppTransition)
+ .overridePendingAppTransitionRemote(adapter, false /* sync */);
+ }
+
+ @Test
+ public void testOverrideTaskFragmentAdapter_overrideEmbeddedActivityWithDiffUid() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+ new TestRemoteAnimationRunner(), 10, 1);
+ setupTaskFragmentRemoteAnimation(organizer, adapter);
+
+ final Task task = createTask(mDisplayContent);
+ // Closing TaskFragment with embedded activity.
+ final TaskFragment taskFragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final ActivityRecord closingActivity = taskFragment1.getTopMostActivity();
+ closingActivity.allDrawn = true;
+ closingActivity.info.applicationInfo.uid = 12345;
+ // Opening TaskFragment with embedded activity with different UID.
+ final TaskFragment taskFragment2 = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final ActivityRecord openingActivity = taskFragment2.getTopMostActivity();
+ openingActivity.info.applicationInfo.uid = 54321;
+ openingActivity.allDrawn = true;
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare a transition.
+ prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment1);
+
+ // Should be overridden.
+ verify(mDisplayContent.mAppTransition)
+ .overridePendingAppTransitionRemote(adapter, false /* sync */);
+ }
+
+ @Test
+ public void testOverrideTaskFragmentAdapter_noOverrideWithTwoApps() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+ new TestRemoteAnimationRunner(), 10, 1);
+ setupTaskFragmentRemoteAnimation(organizer, adapter);
+
+ // Closing activity in Task1.
+ final ActivityRecord closingActivity = createActivityRecord(mDisplayContent);
+ closingActivity.allDrawn = true;
+ // Opening TaskFragment with embedded activity in Task2.
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(
+ createTask(mDisplayContent), organizer);
+ final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
+ openingActivity.allDrawn = true;
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare a transition for TaskFragment.
+ prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
+
+ // Should not be overridden.
+ verify(mDisplayContent.mAppTransition, never())
+ .overridePendingAppTransitionRemote(adapter, false /* sync */);
+ }
+
+ @Test
+ public void testOverrideTaskFragmentAdapter_noOverrideNonEmbeddedActivityWithDiffUid() {
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
+ new TestRemoteAnimationRunner(), 10, 1);
+ setupTaskFragmentRemoteAnimation(organizer, adapter);
+
+ final Task task = createTask(mDisplayContent);
+ // Closing TaskFragment with embedded activity.
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+ final ActivityRecord closingActivity = taskFragment.getTopMostActivity();
+ closingActivity.allDrawn = true;
+ closingActivity.info.applicationInfo.uid = 12345;
+ // Opening non-embedded activity with different UID.
+ final ActivityRecord openingActivity = createActivityRecord(task);
+ openingActivity.info.applicationInfo.uid = 54321;
+ openingActivity.allDrawn = true;
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare a transition.
+ prepareAndTriggerAppTransition(openingActivity, closingActivity, taskFragment);
+
+ // Should not be overridden
+ verify(mDisplayContent.mAppTransition, never())
+ .overridePendingAppTransitionRemote(adapter, false /* sync */);
+ }
+
+ @Test
public void testTransitionGoodToGoForTaskFragments() {
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final Task task = createTask(mDisplayContent);
- final TaskFragment changeTaskFragment = new TaskFragmentBuilder(mAtm)
- .setParentTask(task)
- .createActivityCount(1)
- .setOrganizer(organizer)
- .build();
+ final TaskFragment changeTaskFragment =
+ createTaskFragmentWithEmbeddedActivity(task, organizer);
final TaskFragment emptyTaskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(task)
.setOrganizer(organizer)
.build();
changeTaskFragment.getTopMostActivity().allDrawn = true;
- mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0);
- mDisplayContent.mChangingContainers.add(changeTaskFragment);
spyOn(mDisplayContent.mAppTransition);
spyOn(emptyTaskFragment);
- mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+ prepareAndTriggerAppTransition(
+ null /* openingActivity */, null /* closingActivity*/, changeTaskFragment);
// Transition not ready because there is an empty non-finishing TaskFragment.
verify(mDisplayContent.mAppTransition, never()).goodToGo(anyInt(), any());
@@ -829,4 +921,34 @@
// removed.
verify(mDisplayContent.mAppTransition).goodToGo(anyInt(), any());
}
+
+ /** Registers remote animation for the organizer. */
+ private void setupTaskFragmentRemoteAnimation(TaskFragmentOrganizer organizer,
+ RemoteAnimationAdapter adapter) {
+ final ITaskFragmentOrganizer iOrganizer =
+ ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder());
+ final RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
+ definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, adapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, adapter);
+ mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
+ mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition);
+ }
+
+ private void prepareAndTriggerAppTransition(@Nullable ActivityRecord openingActivity,
+ @Nullable ActivityRecord closingActivity, @Nullable TaskFragment changingTaskFragment) {
+ if (openingActivity != null) {
+ mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_OPEN, 0);
+ mDisplayContent.mOpeningApps.add(openingActivity);
+ }
+ if (closingActivity != null) {
+ mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CLOSE, 0);
+ mDisplayContent.mClosingApps.add(closingActivity);
+ }
+ if (changingTaskFragment != null) {
+ mDisplayContent.mAppTransition.prepareAppTransition(TRANSIT_CHANGE, 0);
+ mDisplayContent.mChangingContainers.add(changingTaskFragment);
+ }
+ mDisplayContent.mAppTransitionController.handleAppTransitionReady();
+ }
}
\ No newline at end of file
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 405d714..fb8bc7b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -35,6 +35,8 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
@@ -42,6 +44,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
import android.graphics.Rect;
import android.os.Binder;
@@ -54,6 +57,7 @@
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
+import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.ITaskFragmentOrganizer;
import android.window.TaskFragmentOrganizer;
@@ -397,7 +401,9 @@
@Test
public void testActivityRecordReparentToTaskFragment() {
final ActivityRecord activity = createActivityRecord(mDc);
+ final SurfaceControl activityLeash = mock(SurfaceControl.class);
activity.setVisibility(true);
+ activity.setSurfaceControl(activityLeash);
final Task task = activity.getTask();
// Add a TaskFragment of half of the Task size.
@@ -412,15 +418,20 @@
final Rect taskBounds = new Rect();
task.getBounds(taskBounds);
taskFragment.setBounds(0, 0, taskBounds.right / 2, taskBounds.bottom);
+ spyOn(taskFragment);
assertTrue(mDc.mChangingContainers.isEmpty());
assertFalse(mDc.mAppTransition.isTransitionSet());
// Schedule app transition when reparent activity to a TaskFragment of different size.
+ final Rect startBounds = new Rect(activity.getBounds());
activity.reparent(taskFragment, POSITION_TOP);
- assertTrue(mDc.mChangingContainers.contains(activity));
+ // It should transit at TaskFragment level with snapshot on the activity surface.
+ verify(taskFragment).initializeChangeTransition(activity.getBounds(), activityLeash);
+ assertTrue(mDc.mChangingContainers.contains(taskFragment));
assertTrue(mDc.mAppTransition.containsTransitRequest(TRANSIT_CHANGE));
+ assertEquals(startBounds, taskFragment.mSurfaceFreezer.mFreezeBounds);
}
private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index e340217..24bbf46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1562,6 +1562,7 @@
final ActivityRecord activity = createActivityRecord(mDisplayContent);
final ActivityRecord recentsActivity = createActivityRecord(mDisplayContent);
recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ doReturn(mock(RecentsAnimationController.class)).when(mWm).getRecentsAnimationController();
// Do not rotate if the recents animation is animating on top.
mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
@@ -2513,7 +2514,7 @@
assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop)));
}
- private static int getRotatedOrientation(DisplayContent dc) {
+ static int getRotatedOrientation(DisplayContent dc) {
return dc.mBaseDisplayWidth > dc.mBaseDisplayHeight
? SCREEN_ORIENTATION_PORTRAIT
: SCREEN_ORIENTATION_LANDSCAPE;
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index 78946fc..1e86522 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -63,7 +63,7 @@
mLetterbox = new Letterbox(mSurfaces, StubTransaction::new,
() -> mAreCornersRounded, () -> Color.valueOf(mColor),
() -> mHasWallpaperBackground, () -> mBlurRadius, () -> mDarkScrimAlpha,
- /* doubleTapCallback= */ () -> {});
+ /* doubleTapCallback= */ x -> {});
mTransaction = spy(StubTransaction.class);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index d88fbee..a680cba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -41,8 +41,8 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -439,6 +439,22 @@
}
@Test
+ public void testCheckRotationAfterCleanup() {
+ mWm.setRecentsAnimationController(mController);
+ spyOn(mDisplayContent.mFixedRotationTransitionListener);
+ doReturn(true).when(mDisplayContent.mFixedRotationTransitionListener)
+ .isTopFixedOrientationRecentsAnimating();
+ // Rotation update is skipped while the recents animation is running.
+ assertFalse(mDisplayContent.getDisplayRotation().updateOrientation(DisplayContentTests
+ .getRotatedOrientation(mDefaultDisplay), false /* forceUpdate */));
+ final int prevRotation = mDisplayContent.getRotation();
+ mWm.cleanupRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+ waitHandlerIdle(mWm.mH);
+ // The display should be updated to the changed orientation after the animation is finished.
+ assertNotEquals(mDisplayContent.getRotation(), prevRotation);
+ }
+
+ @Test
public void testWallpaperHasFixedRotationApplied() {
unblockDisplayRotation(mDefaultDisplay);
mWm.setRecentsAnimationController(mController);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
new file mode 100644
index 0000000..cb209abf
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -0,0 +1,105 @@
+/*
+ * 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.wm;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.mockito.Mockito.clearInvocations;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+import android.window.ITaskFragmentOrganizer;
+import android.window.TaskFragmentOrganizer;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Test class for {@link TaskFragment}.
+ *
+ * Build/Install/Run:
+ * atest WmTests:TaskFragmentTest
+ */
+@MediumTest
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class TaskFragmentTest extends WindowTestsBase {
+
+ private TaskFragmentOrganizer mOrganizer;
+ private TaskFragment mTaskFragment;
+ private SurfaceControl mLeash;
+ @Mock
+ private SurfaceControl.Transaction mTransaction;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mOrganizer = new TaskFragmentOrganizer(Runnable::run);
+ final ITaskFragmentOrganizer iOrganizer =
+ ITaskFragmentOrganizer.Stub.asInterface(mOrganizer.getOrganizerToken().asBinder());
+ mAtm.mWindowOrganizerController.mTaskFragmentOrganizerController
+ .registerOrganizer(iOrganizer);
+ mTaskFragment = new TaskFragmentBuilder(mAtm)
+ .setCreateParentTask()
+ .setOrganizer(mOrganizer)
+ .build();
+ mLeash = mTaskFragment.getSurfaceControl();
+ spyOn(mTaskFragment);
+ doReturn(mTransaction).when(mTaskFragment).getSyncTransaction();
+ doReturn(mTransaction).when(mTaskFragment).getPendingTransaction();
+ }
+
+ @Test
+ public void testOnConfigurationChanged_updateSurface() {
+ final Rect bounds = new Rect(100, 100, 1100, 1100);
+ mTaskFragment.setBounds(bounds);
+
+ verify(mTransaction).setPosition(mLeash, 100, 100);
+ verify(mTransaction).setWindowCrop(mLeash, 1000, 1000);
+ }
+
+ @Test
+ public void testStartChangeTransition_resetSurface() {
+ final Rect startBounds = new Rect(0, 0, 1000, 1000);
+ final Rect endBounds = new Rect(500, 500, 1000, 1000);
+ mTaskFragment.setBounds(startBounds);
+ doReturn(true).when(mTaskFragment).isVisible();
+
+ clearInvocations(mTransaction);
+ mTaskFragment.setBounds(endBounds);
+
+ // Surface reset when prepare transition.
+ verify(mTaskFragment).initializeChangeTransition(startBounds);
+ verify(mTransaction).setPosition(mLeash, 0, 0);
+ verify(mTransaction).setWindowCrop(mLeash, 0, 0);
+
+ clearInvocations(mTransaction);
+ mTaskFragment.mSurfaceFreezer.unfreeze(mTransaction);
+
+ // Update surface after animation.
+ verify(mTransaction).setPosition(mLeash, 500, 500);
+ verify(mTransaction).setWindowCrop(mLeash, 500, 500);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 6626aa4..8ec1bd6c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -699,6 +699,15 @@
return builder.build();
}
+ static TaskFragment createTaskFragmentWithEmbeddedActivity(@NonNull Task parentTask,
+ TaskFragmentOrganizer organizer) {
+ return new TaskFragmentBuilder(parentTask.mAtmService)
+ .setParentTask(parentTask)
+ .createActivityCount(1)
+ .setOrganizer(organizer)
+ .build();
+ }
+
/** Creates a {@link DisplayContent} that supports IME and adds it to the system. */
DisplayContent createNewDisplay() {
return createNewDisplayWithImeSupport(DISPLAY_IME_POLICY_LOCAL);
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 22629dd..0aa62c5 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -257,6 +257,8 @@
// look for MIDI devices
boolean hasMidi = parser.hasMIDIInterface();
+ int midiNumInputs = parser.calculateNumMidiInputs();
+ int midiNumOutputs = parser.calculateNumMidiOutputs();
if (DEBUG) {
Slog.d(TAG, "hasMidi: " + hasMidi + " mHasMidiFeature:" + mHasMidiFeature);
}
@@ -285,7 +287,7 @@
properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice);
UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, properties,
- cardRec.getCardNum(), 0 /*device*/);
+ cardRec.getCardNum(), 0 /*device*/, midiNumInputs, midiNumOutputs);
if (usbMidiDevice != null) {
mMidiDevices.put(deviceAddress, usbMidiDevice);
}
@@ -338,7 +340,8 @@
com.android.internal.R.string.usb_midi_peripheral_product_name));
properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card);
properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device);
- mPeripheralMidiDevice = UsbMidiDevice.create(mContext, properties, card, device);
+ mPeripheralMidiDevice = UsbMidiDevice.create(mContext, properties, card, device,
+ 1 /* numInputs */, 1 /* numOutputs */);
} else if (!enabled && mPeripheralMidiDevice != null) {
IoUtils.closeQuietly(mPeripheralMidiDevice);
mPeripheralMidiDevice = null;
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index f47636e..b61e93b 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -48,8 +48,10 @@
private final int mAlsaCard;
private final int mAlsaDevice;
- private final int mSubdeviceCount;
- private final InputReceiverProxy[] mInputPortReceivers;
+ // USB outputs are MIDI inputs
+ private final InputReceiverProxy[] mMidiInputPortReceivers;
+ private final int mNumInputs;
+ private final int mNumOutputs;
private MidiDeviceServer mServer;
@@ -139,15 +141,14 @@
}
}
- public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) {
- // FIXME - support devices with different number of input and output ports
- int subDeviceCount = nativeGetSubdeviceCount(card, device);
- if (subDeviceCount <= 0) {
- Log.e(TAG, "nativeGetSubdeviceCount failed");
- return null;
- }
-
- UsbMidiDevice midiDevice = new UsbMidiDevice(card, device, subDeviceCount);
+ /**
+ * Creates an UsbMidiDevice based on the input parameters. Read/Write streams
+ * will be created individually as some devices don't have the same number of
+ * inputs and outputs.
+ */
+ public static UsbMidiDevice create(Context context, Bundle properties, int card,
+ int device, int numInputs, int numOutputs) {
+ UsbMidiDevice midiDevice = new UsbMidiDevice(card, device, numInputs, numOutputs);
if (!midiDevice.register(context, properties)) {
IoUtils.closeQuietly(midiDevice);
Log.e(TAG, "createDeviceServer failed");
@@ -156,100 +157,107 @@
return midiDevice;
}
- private UsbMidiDevice(int card, int device, int subdeviceCount) {
+ private UsbMidiDevice(int card, int device, int numInputs, int numOutputs) {
mAlsaCard = card;
mAlsaDevice = device;
- mSubdeviceCount = subdeviceCount;
+ mNumInputs = numInputs;
+ mNumOutputs = numOutputs;
- // FIXME - support devices with different number of input and output ports
- int inputPortCount = subdeviceCount;
- mInputPortReceivers = new InputReceiverProxy[inputPortCount];
- for (int port = 0; port < inputPortCount; port++) {
- mInputPortReceivers[port] = new InputReceiverProxy();
+ // Create MIDI port receivers based on the number of output ports. The
+ // output of USB is the input of MIDI.
+ mMidiInputPortReceivers = new InputReceiverProxy[numOutputs];
+ for (int port = 0; port < numOutputs; port++) {
+ mMidiInputPortReceivers[port] = new InputReceiverProxy();
}
}
private boolean openLocked() {
- // FIXME - support devices with different number of input and output ports
- FileDescriptor[] fileDescriptors = nativeOpen(mAlsaCard, mAlsaDevice, mSubdeviceCount);
+ int inputStreamCount = mNumInputs;
+ // Create an extra stream for unblocking Os.poll()
+ if (inputStreamCount > 0) {
+ inputStreamCount++;
+ }
+ int outputStreamCount = mNumOutputs;
+
+ // The resulting file descriptors will be O_RDONLY following by O_WRONLY
+ FileDescriptor[] fileDescriptors = nativeOpen(mAlsaCard, mAlsaDevice,
+ inputStreamCount, outputStreamCount);
if (fileDescriptors == null) {
Log.e(TAG, "nativeOpen failed");
return false;
}
-
mFileDescriptors = fileDescriptors;
- int inputStreamCount = fileDescriptors.length;
- // last file descriptor returned from nativeOpen() is only used for unblocking Os.poll()
- // in our input thread
- int outputStreamCount = fileDescriptors.length - 1;
mPollFDs = new StructPollfd[inputStreamCount];
mInputStreams = new FileInputStream[inputStreamCount];
+
for (int i = 0; i < inputStreamCount; i++) {
FileDescriptor fd = fileDescriptors[i];
StructPollfd pollfd = new StructPollfd();
pollfd.fd = fd;
- pollfd.events = (short)OsConstants.POLLIN;
+ pollfd.events = (short) OsConstants.POLLIN;
mPollFDs[i] = pollfd;
mInputStreams[i] = new FileInputStream(fd);
}
mOutputStreams = new FileOutputStream[outputStreamCount];
mEventSchedulers = new MidiEventScheduler[outputStreamCount];
- for (int i = 0; i < outputStreamCount; i++) {
- mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]);
+ int curOutputStream = 0;
+ for (int i = 0; i < outputStreamCount; i++) {
+ mOutputStreams[i] = new FileOutputStream(fileDescriptors[inputStreamCount + i]);
MidiEventScheduler scheduler = new MidiEventScheduler();
mEventSchedulers[i] = scheduler;
- mInputPortReceivers[i].setReceiver(scheduler.getReceiver());
+ mMidiInputPortReceivers[i].setReceiver(scheduler.getReceiver());
}
final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();
- // Create input thread which will read from all output ports of the physical device
- new Thread("UsbMidiDevice input thread") {
- @Override
- public void run() {
- byte[] buffer = new byte[BUFFER_SIZE];
- try {
- while (true) {
- // Record time of event immediately after waking.
- long timestamp = System.nanoTime();
- synchronized (mLock) {
- if (!mIsOpen) break;
+ if (inputStreamCount > 0) {
+ // Create input thread which will read from all output ports of the physical device
+ new Thread("UsbMidiDevice input thread") {
+ @Override
+ public void run() {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ try {
+ while (true) {
+ // Record time of event immediately after waking.
+ long timestamp = System.nanoTime();
+ synchronized (mLock) {
+ if (!mIsOpen) break;
- // look for a readable FileDescriptor
- for (int index = 0; index < mPollFDs.length; index++) {
- StructPollfd pfd = mPollFDs[index];
- if ((pfd.revents & (OsConstants.POLLERR
- | OsConstants.POLLHUP)) != 0) {
- break;
- } else if ((pfd.revents & OsConstants.POLLIN) != 0) {
- // clear readable flag
- pfd.revents = 0;
-
- if (index == mInputStreams.length - 1) {
- // last file descriptor is used only for unblocking Os.poll()
+ // look for a readable FileDescriptor
+ for (int index = 0; index < mPollFDs.length; index++) {
+ StructPollfd pfd = mPollFDs[index];
+ if ((pfd.revents & (OsConstants.POLLERR
+ | OsConstants.POLLHUP)) != 0) {
break;
- }
+ } else if ((pfd.revents & OsConstants.POLLIN) != 0) {
+ // clear readable flag
+ pfd.revents = 0;
+ if (index == mInputStreams.length - 1) {
+ // last fd is used only for unblocking Os.poll()
+ break;
+ }
- int count = mInputStreams[index].read(buffer);
- outputReceivers[index].send(buffer, 0, count, timestamp);
+ int count = mInputStreams[index].read(buffer);
+ outputReceivers[index].send(buffer, 0, count, timestamp);
+ }
}
}
- }
- // wait until we have a readable port or we are signalled to close
- Os.poll(mPollFDs, -1 /* infinite timeout */);
- }
- } catch (IOException e) {
- Log.d(TAG, "reader thread exiting");
- } catch (ErrnoException e) {
- Log.d(TAG, "reader thread exiting");
+ // wait until we have a readable port or we are signalled to close
+ Os.poll(mPollFDs, -1 /* infinite timeout */);
+ }
+ } catch (IOException e) {
+ Log.d(TAG, "reader thread exiting");
+ } catch (ErrnoException e) {
+ Log.d(TAG, "reader thread exiting");
+ }
+ Log.d(TAG, "input thread exit");
}
- Log.d(TAG, "input thread exit");
- }
- }.start();
+ }.start();
+ }
// Create output thread for each input port of the physical device
for (int port = 0; port < outputStreamCount; port++) {
@@ -294,7 +302,7 @@
return false;
}
- mServer = midiManager.createDeviceServer(mInputPortReceivers, mSubdeviceCount,
+ mServer = midiManager.createDeviceServer(mMidiInputPortReceivers, mNumInputs,
null, null, properties, MidiDeviceInfo.TYPE_USB, mCallback);
if (mServer == null) {
return false;
@@ -318,7 +326,7 @@
private void closeLocked() {
for (int i = 0; i < mEventSchedulers.length; i++) {
- mInputPortReceivers[i].setReceiver(null);
+ mMidiInputPortReceivers[i].setReceiver(null);
mEventSchedulers[i].close();
}
mEventSchedulers = null;
@@ -354,7 +362,7 @@
dump.end(token);
}
- private static native int nativeGetSubdeviceCount(int card, int device);
- private native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount);
+ private native FileDescriptor[] nativeOpen(int card, int device, int numInputs,
+ int numOutputs);
private native void nativeClose(FileDescriptor[] fileDescriptors);
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 43d5bf3..7250a07 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -656,6 +656,45 @@
return false;
}
+ private int calculateNumMidiPorts(boolean isOutput) {
+ int count = 0;
+ ArrayList<UsbDescriptor> descriptors =
+ getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
+ for (UsbDescriptor descriptor : descriptors) {
+ // ensure that this isn't an unrecognized interface descriptor
+ if (descriptor instanceof UsbInterfaceDescriptor) {
+ UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor;
+ if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
+ for (int i = 0; i < interfaceDescr.getNumEndpoints(); i++) {
+ UsbEndpointDescriptor endpoint = interfaceDescr.getEndpointDescriptor(i);
+ // 0 is output, 1 << 7 is input.
+ if ((endpoint.getDirection() == 0) == isOutput) {
+ count++;
+ }
+ }
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+ return count;
+ }
+
+ /**
+ * @hide
+ */
+ public int calculateNumMidiInputs() {
+ return calculateNumMidiPorts(false /*isOutput*/);
+ }
+
+ /**
+ * @hide
+ */
+ public int calculateNumMidiOutputs() {
+ return calculateNumMidiPorts(true /*isOutput*/);
+ }
+
/**
* @hide
*/
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 9ea2b7b..8445ed4 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -167,6 +167,16 @@
public void onStart() {
publishBinderService(Context.VOICE_INTERACTION_MANAGER_SERVICE, mServiceStub);
publishLocalService(VoiceInteractionManagerInternal.class, new LocalService());
+ mAmInternal.setVoiceInteractionManagerProvider(
+ new ActivityManagerInternal.VoiceInteractionManagerProvider() {
+ @Override
+ public void notifyActivityEventChanged() {
+ if (DEBUG) {
+ Slog.d(TAG, "call notifyActivityEventChanged");
+ }
+ mServiceStub.notifyActivityEventChanged();
+ }
+ });
}
@Override
@@ -386,6 +396,14 @@
return mImpl.supportsLocalVoiceInteraction();
}
+ void notifyActivityEventChanged() {
+ synchronized (this) {
+ if (mImpl == null) return;
+
+ Binder.withCleanCallingIdentity(() -> mImpl.notifyActivityEventChangedLocked());
+ }
+ }
+
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
@@ -1109,6 +1127,40 @@
}
}
+ @Override
+ public void startListeningVisibleActivityChanged(@NonNull IBinder token) {
+ synchronized (this) {
+ if (mImpl == null) {
+ Slog.w(TAG, "startListeningVisibleActivityChanged without running"
+ + " voice interaction service");
+ return;
+ }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mImpl.startListeningVisibleActivityChangedLocked(token);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
+ @Override
+ public void stopListeningVisibleActivityChanged(@NonNull IBinder token) {
+ synchronized (this) {
+ if (mImpl == null) {
+ Slog.w(TAG, "stopListeningVisibleActivityChanged without running"
+ + " voice interaction service");
+ return;
+ }
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ mImpl.stopListeningVisibleActivityChangedLocked(token);
+ } finally {
+ Binder.restoreCallingIdentity(caller);
+ }
+ }
+ }
+
//----------------- Hotword Detection/Validation APIs --------------------------------//
@Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 558a9ac..52c5b6bb 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -414,6 +414,44 @@
return mInfo.getSupportsLocalInteraction();
}
+ public void startListeningVisibleActivityChangedLocked(@NonNull IBinder token) {
+ if (DEBUG) {
+ Slog.d(TAG, "startListeningVisibleActivityChangedLocked: token=" + token);
+ }
+ if (mActiveSession == null || token != mActiveSession.mToken) {
+ Slog.w(TAG, "startListeningVisibleActivityChangedLocked does not match"
+ + " active session");
+ return;
+ }
+ mActiveSession.startListeningVisibleActivityChangedLocked();
+ }
+
+ public void stopListeningVisibleActivityChangedLocked(@NonNull IBinder token) {
+ if (DEBUG) {
+ Slog.d(TAG, "stopListeningVisibleActivityChangedLocked: token=" + token);
+ }
+ if (mActiveSession == null || token != mActiveSession.mToken) {
+ Slog.w(TAG, "stopListeningVisibleActivityChangedLocked does not match"
+ + " active session");
+ return;
+ }
+ mActiveSession.stopListeningVisibleActivityChangedLocked();
+ }
+
+ public void notifyActivityEventChangedLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyActivityEventChangedLocked");
+ }
+ if (mActiveSession == null || !mActiveSession.mShown) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyActivityEventChangedLocked not allowed on no session or"
+ + " hidden session");
+ }
+ return;
+ }
+ mActiveSession.notifyActivityEventChangedLocked();
+ }
+
public void updateStateLocked(
@NonNull Identity voiceInteractorIdentity,
@Nullable PersistableBundle options,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 08e9703..90ccec8 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -56,6 +56,7 @@
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.IVoiceInteractionSessionService;
+import android.service.voice.VisibleActivityInfo;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionSession;
import android.util.Slog;
@@ -71,16 +72,20 @@
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.ActivityAssistInfo;
+import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.PrintWriter;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
final class VoiceInteractionSessionConnection implements ServiceConnection,
AssistDataRequesterCallbacks {
static final String TAG = "VoiceInteractionServiceManager";
+ static final boolean DEBUG = false;
static final int POWER_BOOST_TIMEOUT_MS = Integer.parseInt(
System.getProperty("vendor.powerhal.interaction.max", "200"));
static final int BOOST_TIMEOUT_MS = 300;
@@ -114,6 +119,10 @@
ArrayList<IVoiceInteractionSessionShowCallback> mPendingShowCallbacks = new ArrayList<>();
private List<ActivityAssistInfo> mPendingHandleAssistWithoutData = new ArrayList<>();
AssistDataRequester mAssistDataRequester;
+ private boolean mListeningVisibleActivity;
+ private final ScheduledExecutorService mScheduledExecutorService =
+ Executors.newSingleThreadScheduledExecutor();
+ private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>();
private final PowerManagerInternal mPowerManagerInternal;
private PowerBoostSetter mSetPowerBoostRunnable;
private final Handler mFgHandler;
@@ -496,6 +505,8 @@
}
public void cancelLocked(boolean finishTask) {
+ mListeningVisibleActivity = false;
+ mVisibleActivityInfos.clear();
hideLocked();
mCanceled = true;
if (mBound) {
@@ -569,6 +580,156 @@
mPendingShowCallbacks.clear();
}
+ void startListeningVisibleActivityChangedLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "startListeningVisibleActivityChangedLocked");
+ }
+ mListeningVisibleActivity = true;
+ mVisibleActivityInfos.clear();
+
+ mScheduledExecutorService.execute(() -> {
+ if (DEBUG) {
+ Slog.d(TAG, "call updateVisibleActivitiesLocked from enable listening");
+ }
+ synchronized (mLock) {
+ updateVisibleActivitiesLocked();
+ }
+ });
+ }
+
+ void stopListeningVisibleActivityChangedLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "stopListeningVisibleActivityChangedLocked");
+ }
+ mListeningVisibleActivity = false;
+ mVisibleActivityInfos.clear();
+ }
+
+ void notifyActivityEventChangedLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyActivityEventChangedLocked");
+ }
+ if (!mListeningVisibleActivity) {
+ if (DEBUG) {
+ Slog.d(TAG, "not enable listening visible activity");
+ }
+ return;
+ }
+ mScheduledExecutorService.execute(() -> {
+ if (DEBUG) {
+ Slog.d(TAG, "call updateVisibleActivitiesLocked from activity event");
+ }
+ synchronized (mLock) {
+ updateVisibleActivitiesLocked();
+ }
+ });
+ }
+
+ private List<VisibleActivityInfo> getVisibleActivityInfosLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "getVisibleActivityInfosLocked");
+ }
+ List<ActivityAssistInfo> allVisibleActivities =
+ LocalServices.getService(ActivityTaskManagerInternal.class)
+ .getTopVisibleActivities();
+ if (DEBUG) {
+ Slog.d(TAG,
+ "getVisibleActivityInfosLocked: allVisibleActivities=" + allVisibleActivities);
+ }
+ if (allVisibleActivities == null || allVisibleActivities.isEmpty()) {
+ Slog.w(TAG, "no visible activity");
+ return null;
+ }
+ final int count = allVisibleActivities.size();
+ final List<VisibleActivityInfo> visibleActivityInfos = new ArrayList<>(count);
+ for (int i = 0; i < count; i++) {
+ ActivityAssistInfo info = allVisibleActivities.get(i);
+ if (DEBUG) {
+ Slog.d(TAG, " : activityToken=" + info.getActivityToken()
+ + ", assistToken=" + info.getAssistToken()
+ + ", taskId=" + info.getTaskId());
+ }
+ visibleActivityInfos.add(
+ new VisibleActivityInfo(info.getTaskId(), info.getAssistToken()));
+ }
+ return visibleActivityInfos;
+ }
+
+ private void updateVisibleActivitiesLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "updateVisibleActivitiesLocked");
+ }
+ if (mSession == null) {
+ return;
+ }
+ if (!mShown || !mListeningVisibleActivity || mCanceled) {
+ return;
+ }
+ final List<VisibleActivityInfo> newVisibleActivityInfos = getVisibleActivityInfosLocked();
+
+ if (newVisibleActivityInfos == null || newVisibleActivityInfos.isEmpty()) {
+ updateVisibleActivitiesChangedLocked(mVisibleActivityInfos,
+ VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
+ mVisibleActivityInfos.clear();
+ return;
+ }
+ if (mVisibleActivityInfos.isEmpty()) {
+ updateVisibleActivitiesChangedLocked(newVisibleActivityInfos,
+ VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
+ mVisibleActivityInfos.addAll(newVisibleActivityInfos);
+ return;
+ }
+
+ final List<VisibleActivityInfo> addedActivities = new ArrayList<>();
+ final List<VisibleActivityInfo> removedActivities = new ArrayList<>();
+
+ removedActivities.addAll(mVisibleActivityInfos);
+ for (int i = 0; i < newVisibleActivityInfos.size(); i++) {
+ final VisibleActivityInfo candidateVisibleActivityInfo = newVisibleActivityInfos.get(i);
+ if (!removedActivities.isEmpty() && removedActivities.contains(
+ candidateVisibleActivityInfo)) {
+ removedActivities.remove(candidateVisibleActivityInfo);
+ } else {
+ addedActivities.add(candidateVisibleActivityInfo);
+ }
+ }
+
+ if (!addedActivities.isEmpty()) {
+ updateVisibleActivitiesChangedLocked(addedActivities,
+ VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
+ }
+ if (!removedActivities.isEmpty()) {
+ updateVisibleActivitiesChangedLocked(removedActivities,
+ VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
+ }
+
+ mVisibleActivityInfos.clear();
+ mVisibleActivityInfos.addAll(newVisibleActivityInfos);
+ }
+
+ private void updateVisibleActivitiesChangedLocked(
+ List<VisibleActivityInfo> visibleActivityInfos, int type) {
+ if (visibleActivityInfos == null || visibleActivityInfos.isEmpty()) {
+ return;
+ }
+ if (mSession == null) {
+ return;
+ }
+ try {
+ for (int i = 0; i < visibleActivityInfos.size(); i++) {
+ mSession.updateVisibleActivityInfo(visibleActivityInfos.get(i), type);
+ }
+ } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.w(TAG, "updateVisibleActivitiesChangedLocked RemoteException : " + e);
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "updateVisibleActivitiesChangedLocked type=" + type + ", count="
+ + visibleActivityInfos.size());
+ }
+ }
+
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mLock) {
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 4d81b5e..7a424c8 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -308,6 +308,12 @@
return checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
context, subId, callingPackage, callingFeatureId, message, false, reportFailure);
}
+
+ private static void throwSecurityExceptionAsUidDoesNotHaveAccess(String message, int uid) {
+ throw new SecurityException(message + ": The uid " + uid
+ + " does not meet the requirements to access device identifiers.");
+ }
+
/**
* Checks whether the app with the given pid/uid can read device identifiers.
*
@@ -343,9 +349,14 @@
LegacyPermissionManager permissionManager = (LegacyPermissionManager)
context.getSystemService(Context.LEGACY_PERMISSION_SERVICE);
- if (permissionManager.checkDeviceIdentifierAccess(callingPackage, message, callingFeatureId,
- pid, uid) == PackageManager.PERMISSION_GRANTED) {
- return true;
+ try {
+ if (permissionManager.checkDeviceIdentifierAccess(callingPackage, message,
+ callingFeatureId,
+ pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ } catch (SecurityException se) {
+ throwSecurityExceptionAsUidDoesNotHaveAccess(message, uid);
}
if (reportFailure) {
@@ -410,8 +421,8 @@
return false;
}
}
- throw new SecurityException(message + ": The user " + uid
- + " does not meet the requirements to access device identifiers.");
+ throwSecurityExceptionAsUidDoesNotHaveAccess(message, uid);
+ return true;
}
/**
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 1ef04be..097d363 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -2059,8 +2059,7 @@
| TYPE_FOTA | TYPE_IMS | TYPE_CBS | TYPE_IA | TYPE_EMERGENCY | TYPE_MCX
| TYPE_XCAP | TYPE_VSIM | TYPE_BIP | TYPE_ENTERPRISE)) == 0
|| TextUtils.isEmpty(mApnName) || TextUtils.isEmpty(mEntryName)) {
- throw new IllegalArgumentException("mApName=" + mApnName + ", mEntryName="
- + mEntryName + ", mApnTypeBitmask=" + mApnTypeBitmask);
+ return null;
}
return new ApnSetting(this);
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 4d4f266..fd8abc6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -16,6 +16,7 @@
package com.android.server.wm.flicker.rotation
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
@@ -25,11 +26,13 @@
import com.android.server.wm.flicker.annotation.Group3
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.common.FlickerComponentName
import org.junit.FixMethodOrder
+import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -78,6 +81,9 @@
class ChangeAppRotationTest(
testSpec: FlickerTestParameter
) : RotationTransition(testSpec) {
+ @get:Rule
+ val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec)
+
override val testApp = SimpleAppHelper(instrumentation)
override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = {
@@ -89,6 +95,24 @@
}
}
+ @Postsubmit
+ @Test
+ fun runPresubmitAssertion() {
+ flickerRule.checkPresubmitAssertions()
+ }
+
+ @Postsubmit
+ @Test
+ fun runPostsubmitAssertion() {
+ flickerRule.checkPostsubmitAssertions()
+ }
+
+ @FlakyTest
+ @Test
+ fun runFlakyAssertion() {
+ flickerRule.checkFlakyAssertions()
+ }
+
/** {@inheritDoc} */
@FlakyTest(bugId = 190185577)
@Test
@@ -109,6 +133,7 @@
.isVisible(FlickerComponentName.ROTATION)
.then()
.isVisible(testApp.component)
+ .isInvisible(FlickerComponentName.ROTATION)
}
}